15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/context_group.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h"
11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/sys_info.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/common/id_allocator.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/buffer_manager.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/framebuffer_manager.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/gpu_switches.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/mailbox_manager.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/memory_tracking.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/program_manager.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/renderbuffer_manager.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/shader_manager.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/texture_manager.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/command_buffer/service/transfer_buffer_manager.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gl/gl_implementation.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace gpu {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace gles2 {
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ContextGroup::ContextGroup(
311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const scoped_refptr<MailboxManager>& mailbox_manager,
321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const scoped_refptr<MemoryTracker>& memory_tracker,
331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const scoped_refptr<ShaderTranslatorCache>& shader_translator_cache,
341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const scoped_refptr<FeatureInfo>& feature_info,
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool bind_generates_resource)
361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    : mailbox_manager_(mailbox_manager),
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      memory_tracker_(memory_tracker),
38c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      shader_translator_cache_(shader_translator_cache),
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      enforce_gl_minimums_(CommandLine::ForCurrentProcess()->HasSwitch(
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          switches::kEnforceGLMinimums)),
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bind_generates_resource_(bind_generates_resource),
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max_vertex_attribs_(0u),
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max_texture_units_(0u),
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max_texture_image_units_(0u),
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max_vertex_texture_image_units_(0u),
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max_fragment_uniform_vectors_(0u),
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max_varying_vectors_(0u),
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max_vertex_uniform_vectors_(0u),
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      max_color_attachments_(1u),
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      max_draw_buffers_(1u),
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      program_cache_(NULL),
521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      feature_info_(feature_info),
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      draw_buffer_(GL_BACK) {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (!mailbox_manager_.get())
561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      mailbox_manager_ = new MailboxManager;
571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (!feature_info.get())
581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      feature_info_ = new FeatureInfo;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TransferBufferManager* manager = new TransferBufferManager();
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    transfer_buffer_manager_.reset(manager);
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    manager->Initialize();
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  id_namespaces_[id_namespaces::kBuffers].reset(new IdAllocator);
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  id_namespaces_[id_namespaces::kFramebuffers].reset(new IdAllocator);
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  id_namespaces_[id_namespaces::kProgramsAndShaders].reset(
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new NonReusedIdAllocator);
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  id_namespaces_[id_namespaces::kRenderbuffers].reset(new IdAllocator);
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  id_namespaces_[id_namespaces::kTextures].reset(new IdAllocator);
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  id_namespaces_[id_namespaces::kQueries].reset(new IdAllocator);
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  id_namespaces_[id_namespaces::kVertexArrays].reset(new IdAllocator);
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void GetIntegerv(GLenum pname, uint32* var) {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GLint value = 0;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  glGetIntegerv(pname, &value);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *var = value;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ContextGroup::Initialize(
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GLES2Decoder* decoder,
8258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    const DisallowedFeatures& disallowed_features) {
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If we've already initialized the group just add the context.
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (HaveContexts()) {
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    decoders_.push_back(base::AsWeakPtr<GLES2Decoder>(decoder));
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!feature_info_->Initialize(disallowed_features)) {
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "ContextGroup::Initialize failed because FeatureInfo "
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "initialization failed.";
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const GLint kMinRenderbufferSize = 512;  // GL says 1 pixel!
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GLint max_renderbuffer_size = 0;
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!QueryGLFeature(
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GL_MAX_RENDERBUFFER_SIZE, kMinRenderbufferSize,
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      &max_renderbuffer_size)) {
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "ContextGroup::Initialize failed because maximum "
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "renderbuffer size too small.";
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GLint max_samples = 0;
1057dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (feature_info_->feature_flags().chromium_framebuffer_multisample ||
1067dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      feature_info_->feature_flags().multisampled_render_to_texture) {
1077dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (feature_info_->feature_flags(
1087dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            ).use_img_for_multisampled_render_to_texture) {
1097dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      glGetIntegerv(GL_MAX_SAMPLES_IMG, &max_samples);
1107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    } else {
1117dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      glGetIntegerv(GL_MAX_SAMPLES, &max_samples);
1127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    }
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (feature_info_->feature_flags().ext_draw_buffers) {
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &max_color_attachments_);
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (max_color_attachments_ < 1)
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      max_color_attachments_ = 1;
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GetIntegerv(GL_MAX_DRAW_BUFFERS_ARB, &max_draw_buffers_);
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (max_draw_buffers_ < 1)
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      max_draw_buffers_ = 1;
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    draw_buffer_ = GL_BACK;
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  const bool depth24_supported = feature_info_->feature_flags().oes_depth24;
12668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
127868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  buffer_manager_.reset(
128868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      new BufferManager(memory_tracker_.get(), feature_info_.get()));
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  framebuffer_manager_.reset(
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new FramebufferManager(max_draw_buffers_, max_color_attachments_));
131868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  renderbuffer_manager_.reset(new RenderbufferManager(
13268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      memory_tracker_.get(), max_renderbuffer_size, max_samples,
13368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      depth24_supported));
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  shader_manager_.reset(new ShaderManager());
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Lookup GL things we need to know.
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const GLint kGLES2RequiredMinimumVertexAttribs = 8u;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!QueryGLFeatureU(
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GL_MAX_VERTEX_ATTRIBS, kGLES2RequiredMinimumVertexAttribs,
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      &max_vertex_attribs_)) {
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "ContextGroup::Initialize failed because too few "
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "vertex attributes supported.";
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const GLuint kGLES2RequiredMinimumTextureUnits = 8u;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!QueryGLFeatureU(
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, kGLES2RequiredMinimumTextureUnits,
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      &max_texture_units_)) {
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "ContextGroup::Initialize failed because too few "
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "texture units supported.";
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GLint max_texture_size = 0;
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GLint max_cube_map_texture_size = 0;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const GLint kMinTextureSize = 2048;  // GL actually says 64!?!?
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const GLint kMinCubeMapSize = 256;  // GL actually says 16!?!?
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!QueryGLFeature(
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GL_MAX_TEXTURE_SIZE, kMinTextureSize, &max_texture_size) ||
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !QueryGLFeature(
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GL_MAX_CUBE_MAP_TEXTURE_SIZE, kMinCubeMapSize,
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      &max_cube_map_texture_size)) {
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "ContextGroup::Initialize failed because maximum texture size"
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "is too small.";
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (feature_info_->workarounds().max_texture_size) {
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    max_texture_size = std::min(
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        max_texture_size, feature_info_->workarounds().max_texture_size);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (feature_info_->workarounds().max_cube_map_texture_size) {
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    max_cube_map_texture_size = std::min(
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        max_cube_map_texture_size,
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        feature_info_->workarounds().max_cube_map_texture_size);
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
179868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  texture_manager_.reset(new TextureManager(memory_tracker_.get(),
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            feature_info_.get(),
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            max_texture_size,
182c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                                            max_cube_map_texture_size,
183c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                                            bind_generates_resource_));
18490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  texture_manager_->set_framebuffer_manager(framebuffer_manager_.get());
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const GLint kMinTextureImageUnits = 8;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const GLint kMinVertexTextureImageUnits = 0;
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!QueryGLFeatureU(
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GL_MAX_TEXTURE_IMAGE_UNITS, kMinTextureImageUnits,
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      &max_texture_image_units_) ||
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !QueryGLFeatureU(
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, kMinVertexTextureImageUnits,
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      &max_vertex_texture_image_units_)) {
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "ContextGroup::Initialize failed because too few "
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "texture units.";
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2) {
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS,
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        &max_fragment_uniform_vectors_);
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetIntegerv(GL_MAX_VARYING_VECTORS, &max_varying_vectors_);
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &max_vertex_uniform_vectors_);
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetIntegerv(
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &max_fragment_uniform_vectors_);
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    max_fragment_uniform_vectors_ /= 4;
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetIntegerv(GL_MAX_VARYING_FLOATS, &max_varying_vectors_);
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    max_varying_vectors_ /= 4;
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &max_vertex_uniform_vectors_);
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    max_vertex_uniform_vectors_ /= 4;
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const GLint kMinFragmentUniformVectors = 16;
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const GLint kMinVaryingVectors = 8;
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const GLint kMinVertexUniformVectors = 128;
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!CheckGLFeatureU(
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kMinFragmentUniformVectors, &max_fragment_uniform_vectors_) ||
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !CheckGLFeatureU(kMinVaryingVectors, &max_varying_vectors_) ||
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !CheckGLFeatureU(
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kMinVertexUniformVectors, &max_vertex_uniform_vectors_)) {
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "ContextGroup::Initialize failed because too few "
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "uniforms or varyings supported.";
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2270529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  // Some shaders in Skia need more than the min available vertex and
2280529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  // fragment shader uniform vectors in case of OSMesa GL Implementation
2290529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  if (feature_info_->workarounds().max_fragment_uniform_vectors) {
2300529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    max_fragment_uniform_vectors_ = std::min(
2310529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        max_fragment_uniform_vectors_,
2320529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        static_cast<uint32>(
2330529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            feature_info_->workarounds().max_fragment_uniform_vectors));
2340529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  }
2350529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  if (feature_info_->workarounds().max_varying_vectors) {
2360529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    max_varying_vectors_ = std::min(
2370529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        max_varying_vectors_,
2380529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        static_cast<uint32>(feature_info_->workarounds().max_varying_vectors));
2390529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  }
2400529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  if (feature_info_->workarounds().max_vertex_uniform_vectors) {
24190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    max_vertex_uniform_vectors_ =
2420529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        std::min(max_vertex_uniform_vectors_,
2430529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                 static_cast<uint32>(
2440529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                     feature_info_->workarounds().max_vertex_uniform_vectors));
24590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
24690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
24758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  program_manager_.reset(new ProgramManager(
24858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      program_cache_, max_varying_vectors_));
24958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!texture_manager_->Initialize()) {
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Context::Group::Initialize failed because texture manager "
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "failed to initialize.";
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  decoders_.push_back(base::AsWeakPtr<GLES2Decoder>(decoder));
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool IsNull(const base::WeakPtr<gles2::GLES2Decoder>& decoder) {
263868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return !decoder.get();
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
266868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)template <typename T>
267868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)class WeakPtrEquals {
268868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) public:
269868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  explicit WeakPtrEquals(T* t) : t_(t) {}
270868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
271868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  bool operator()(const base::WeakPtr<T>& t) {
272868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return t.get() == t_;
273868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
274868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
275868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) private:
276868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  T* const t_;
277868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)};
278868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace anonymous
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ContextGroup::HaveContexts() {
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  decoders_.erase(std::remove_if(decoders_.begin(), decoders_.end(), IsNull),
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  decoders_.end());
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return !decoders_.empty();
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ContextGroup::Destroy(GLES2Decoder* decoder, bool have_context) {
288868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  decoders_.erase(std::remove_if(decoders_.begin(), decoders_.end(),
289868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                 WeakPtrEquals<gles2::GLES2Decoder>(decoder)),
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  decoders_.end());
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If we still have contexts do nothing.
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (HaveContexts()) {
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (buffer_manager_ != NULL) {
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    buffer_manager_->Destroy(have_context);
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    buffer_manager_.reset();
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (framebuffer_manager_ != NULL) {
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    framebuffer_manager_->Destroy(have_context);
30390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if (texture_manager_)
30490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      texture_manager_->set_framebuffer_manager(NULL);
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    framebuffer_manager_.reset();
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (renderbuffer_manager_ != NULL) {
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    renderbuffer_manager_->Destroy(have_context);
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    renderbuffer_manager_.reset();
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (texture_manager_ != NULL) {
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    texture_manager_->Destroy(have_context);
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    texture_manager_.reset();
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (program_manager_ != NULL) {
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    program_manager_->Destroy(have_context);
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    program_manager_.reset();
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (shader_manager_ != NULL) {
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    shader_manager_->Destroy(have_context);
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    shader_manager_.reset();
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memory_tracker_ = NULL;
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IdAllocatorInterface* ContextGroup::GetIdAllocator(unsigned namespace_id) {
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (namespace_id >= arraysize(id_namespaces_))
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return id_namespaces_[namespace_id].get();
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)uint32 ContextGroup::GetMemRepresented() const {
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint32 total = 0;
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (buffer_manager_.get())
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    total += buffer_manager_->mem_represented();
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (renderbuffer_manager_.get())
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    total += renderbuffer_manager_->mem_represented();
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (texture_manager_.get())
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    total += texture_manager_->mem_represented();
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return total;
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ContextGroup::LoseContexts(GLenum reset_status) {
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t ii = 0; ii < decoders_.size(); ++ii) {
351868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (decoders_[ii].get()) {
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      decoders_[ii]->LoseContext(reset_status);
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ContextGroup::~ContextGroup() {
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CHECK(!HaveContexts());
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ContextGroup::CheckGLFeature(GLint min_required, GLint* v) {
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GLint value = *v;
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (enforce_gl_minimums_) {
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    value = std::min(min_required, value);
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *v = value;
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return value >= min_required;
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ContextGroup::CheckGLFeatureU(GLint min_required, uint32* v) {
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GLint value = *v;
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (enforce_gl_minimums_) {
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    value = std::min(min_required, value);
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *v = value;
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return value >= min_required;
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ContextGroup::QueryGLFeature(
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GLenum pname, GLint min_required, GLint* v) {
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GLint value = 0;
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  glGetIntegerv(pname, &value);
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *v = value;
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return CheckGLFeature(min_required, v);
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ContextGroup::QueryGLFeatureU(
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GLenum pname, GLint min_required, uint32* v) {
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint32 value = 0;
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetIntegerv(pname, &value);
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool result = CheckGLFeatureU(min_required, &value);
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *v = value;
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace gles2
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace gpu
398