/* * Copyright 2009, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // This file implements the texture-related GAPI functions on GL. #include "gpu/command_buffer/service/precompile.h" #include "gpu/command_buffer/service/gapi_gl.h" #include "gpu/command_buffer/service/texture_gl.h" namespace command_buffer { namespace o3d { namespace { // Gets the GL internal format, format and type corresponding to a command // buffer texture format. bool GetGLFormatType(texture::Format format, GLenum *internal_format, GLenum *gl_format, GLenum *gl_type) { switch (format) { case texture::kXRGB8: { *internal_format = GL_RGB; *gl_format = GL_BGRA; *gl_type = GL_UNSIGNED_BYTE; break; } case texture::kARGB8: { *internal_format = GL_RGBA; *gl_format = GL_BGRA; *gl_type = GL_UNSIGNED_BYTE; break; } case texture::kABGR16F: { *internal_format = GL_RGBA16F_ARB; *gl_format = GL_RGBA; *gl_type = GL_HALF_FLOAT_ARB; break; } case texture::kR32F: { *internal_format = GL_LUMINANCE32F_ARB; *gl_format = GL_LUMINANCE; *gl_type = GL_FLOAT; break; } case texture::kABGR32F: { *internal_format = GL_RGBA32F_ARB; *gl_format = GL_BGRA; *gl_type = GL_FLOAT; break; } case texture::kDXT1: { *internal_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; *gl_format = 0; *gl_type = 0; break; } // TODO(petersont): Add DXT3/5 support. default: return false; } return true; } // Helper class used to prepare image data to match the layout that // glTexImage* and glCompressedTexImage* expect. class SetImageHelper { public: SetImageHelper() : buffer_(NULL), image_data_(NULL), image_size_(0) { } // Initializes the helper with the input data, re-using the input buffer if // possible, or copying it into a temporary one. bool Initialize(const MipLevelInfo &mip_info, const Volume& volume, unsigned int row_pitch, unsigned int slice_pitch, unsigned int src_size, const void *data) { TransferInfo src_transfer_info; MakeTransferInfo(&src_transfer_info, mip_info, volume, row_pitch, slice_pitch); if (!CheckVolume(mip_info, volume) || src_size < src_transfer_info.total_size) return false; if (!src_transfer_info.packed) { TransferInfo dst_transfer_info; MakePackedTransferInfo(&dst_transfer_info, mip_info, volume); buffer_.reset(new unsigned char[dst_transfer_info.total_size]); TransferVolume(volume, mip_info, dst_transfer_info, buffer_.get(), src_transfer_info, data); image_data_ = buffer_.get(); image_size_ = dst_transfer_info.total_size; } else { image_data_ = data; image_size_ = src_transfer_info.total_size; } return true; } // Gets the buffer that contains the data in the GL format. const void *image_data() { return image_data_; } // Gets the size of the buffer as GL expects it. unsigned int image_size() { return image_size_; } private: scoped_array<unsigned char> buffer_; const void *image_data_; unsigned int image_size_; DISALLOW_COPY_AND_ASSIGN(SetImageHelper); }; // Helper class used to retrieve image data to match the layout that // glGetTexImage and glGetCompressedTexImage expect. class GetImageHelper { public: GetImageHelper() : dst_data_(NULL), buffer_(NULL), image_data_(NULL) { memset(&mip_info_, 0, sizeof(mip_info_)); memset(&volume_, 0, sizeof(volume_)); memset(&dst_transfer_info_, 0, sizeof(dst_transfer_info_)); memset(&src_transfer_info_, 0, sizeof(src_transfer_info_)); } // Initialize the helper to make available a buffer to get the data from GL. // It will re-use the passed in buffer if the layout matches GL, or allocate // a temporary one. bool Initialize(const MipLevelInfo &mip_info, const Volume& volume, unsigned int row_pitch, unsigned int slice_pitch, unsigned int dst_size, void *dst_data) { mip_info_ = mip_info; volume_ = volume; dst_data_ = dst_data; MakeTransferInfo(&dst_transfer_info_, mip_info, volume, row_pitch, slice_pitch); if (!CheckVolume(mip_info, volume) || dst_size < dst_transfer_info_.total_size) return false; if (!IsFullVolume(mip_info, volume) || !dst_transfer_info_.packed) { // We can only retrieve the full image from GL. Volume full_volume = { 0, 0, 0, mip_info.width, mip_info.height, mip_info.depth }; MakePackedTransferInfo(&src_transfer_info_, mip_info, full_volume); buffer_.reset(new unsigned char[src_transfer_info_.total_size]); image_data_ = buffer_.get(); } else { image_data_ = dst_data; } return true; } // Finalize the helper, copying the data into the final buffer if needed. void Finalize() { if (!buffer_.get()) return; unsigned int offset = volume_.x / mip_info_.block_size_x * mip_info_.block_bpp + volume_.y / mip_info_.block_size_y * src_transfer_info_.row_pitch + volume_.z * src_transfer_info_.slice_pitch; src_transfer_info_.row_size = dst_transfer_info_.row_size; TransferVolume(volume_, mip_info_, dst_transfer_info_, dst_data_, src_transfer_info_, buffer_.get() + offset); } // Gets the buffer that can receive the data from GL. void *image_data() { return image_data_; } private: MipLevelInfo mip_info_; Volume volume_; TransferInfo dst_transfer_info_; TransferInfo src_transfer_info_; void *dst_data_; scoped_array<unsigned char> buffer_; void *image_data_; DISALLOW_COPY_AND_ASSIGN(GetImageHelper); }; } // anonymous namespace TextureGL::~TextureGL() { glDeleteTextures(1, &gl_texture_); CHECK_GL_ERROR(); } Texture2DGL *Texture2DGL::Create(unsigned int width, unsigned int height, unsigned int levels, texture::Format format, unsigned int flags, bool enable_render_surfaces) { DCHECK_GT(width, 0U); DCHECK_GT(height, 0U); DCHECK_GT(levels, 0U); GLenum gl_internal_format = 0; GLenum gl_format = 0; GLenum gl_type = 0; bool r = GetGLFormatType(format, &gl_internal_format, &gl_format, &gl_type); DCHECK(r); // Was checked in the decoder. GLuint gl_texture = 0; glGenTextures(1, &gl_texture); glBindTexture(GL_TEXTURE_2D, gl_texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels - 1); // glCompressedTexImage2D does't accept NULL as a parameter, so we need // to pass in some data. scoped_array<unsigned char> buffer; MipLevelInfo mip_info; MakeMipLevelInfo(&mip_info, format, width, height, 1, 0); unsigned int size = GetMipLevelSize(mip_info); buffer.reset(new unsigned char[size]); memset(buffer.get(), 0, size); unsigned int mip_width = width; unsigned int mip_height = height; for (unsigned int i = 0; i < levels; ++i) { if (gl_format) { glTexImage2D(GL_TEXTURE_2D, i, gl_internal_format, mip_width, mip_height, 0, gl_format, gl_type, buffer.get()); } else { MipLevelInfo mip_info; MakeMipLevelInfo(&mip_info, format, width, height, 1, i); unsigned int size = GetMipLevelSize(mip_info); glCompressedTexImage2D(GL_TEXTURE_2D, i, gl_internal_format, mip_width, mip_height, 0, size, buffer.get()); } mip_width = std::max(1U, mip_width >> 1); mip_height = std::max(1U, mip_height >> 1); } return new Texture2DGL( levels, format, enable_render_surfaces, flags, width, height, gl_texture); } // Sets data into a 2D texture resource. bool Texture2DGL::SetData(const Volume& volume, unsigned int level, texture::Face face, unsigned int row_pitch, unsigned int slice_pitch, unsigned int size, const void *data) { if (level >= levels()) return false; MipLevelInfo mip_info; MakeMipLevelInfo(&mip_info, format(), width_, height_, 1, level); SetImageHelper helper; if (!helper.Initialize(mip_info, volume, row_pitch, slice_pitch, size, data)) return false; GLenum gl_internal_format = 0; GLenum gl_format = 0; GLenum gl_type = 0; bool r = GetGLFormatType(format(), &gl_internal_format, &gl_format, &gl_type); DCHECK(r); glBindTexture(GL_TEXTURE_2D, gl_texture_); if (gl_format) { glTexSubImage2D(GL_TEXTURE_2D, level, volume.x, volume.y, volume.width, volume.height, gl_format, gl_type, helper.image_data()); } else { glCompressedTexSubImage2D(GL_TEXTURE_2D, level, volume.x, volume.y, volume.width, volume.height, gl_internal_format, helper.image_size(), helper.image_data()); } return true; } bool Texture2DGL::GetData(const Volume& volume, unsigned int level, texture::Face face, unsigned int row_pitch, unsigned int slice_pitch, unsigned int size, void *data) { if (level >= levels()) return false; MipLevelInfo mip_info; MakeMipLevelInfo(&mip_info, format(), width_, height_, 1, level); GetImageHelper helper; if (!helper.Initialize(mip_info, volume, row_pitch, slice_pitch, size, data)) return false; GLenum gl_internal_format = 0; GLenum gl_format = 0; GLenum gl_type = 0; bool r = GetGLFormatType(format(), &gl_internal_format, &gl_format, &gl_type); DCHECK(r); glBindTexture(GL_TEXTURE_2D, gl_texture_); if (gl_format) { glGetTexImage(GL_TEXTURE_2D, level, gl_format, gl_type, helper.image_data()); } else { glGetCompressedTexImage(GL_TEXTURE_2D, level, helper.image_data()); } helper.Finalize(); return true; } bool Texture2DGL::CreateRenderSurface(int width, int height, int mip_level, int side) { return false; } bool Texture2DGL::InstallFrameBufferObjects( RenderSurfaceGL *gl_surface) { ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, gl_texture_, gl_surface->mip_level()); GLenum framebuffer_status = ::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (GL_FRAMEBUFFER_COMPLETE_EXT != framebuffer_status) { return false; } return true; } Texture3DGL *Texture3DGL::Create(unsigned int width, unsigned int height, unsigned int depth, unsigned int levels, texture::Format format, unsigned int flags, bool enable_render_surfaces) { DCHECK_GT(width, 0U); DCHECK_GT(height, 0U); DCHECK_GT(depth, 0U); DCHECK_GT(levels, 0U); GLenum gl_internal_format = 0; GLenum gl_format = 0; GLenum gl_type = 0; bool r = GetGLFormatType(format, &gl_internal_format, &gl_format, &gl_type); DCHECK(r); // Was checked in the decoder. GLuint gl_texture = 0; glGenTextures(1, &gl_texture); glBindTexture(GL_TEXTURE_3D, gl_texture); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, levels - 1); // glCompressedTexImage3D does't accept NULL as a parameter, so we need // to pass in some data. scoped_array<unsigned char> buffer; MipLevelInfo mip_info; MakeMipLevelInfo(&mip_info, format, width, height, depth, 0); unsigned int size = GetMipLevelSize(mip_info); buffer.reset(new unsigned char[size]); memset(buffer.get(), 0, size); unsigned int mip_width = width; unsigned int mip_height = height; unsigned int mip_depth = depth; for (unsigned int i = 0; i < levels; ++i) { if (gl_format) { glTexImage3D(GL_TEXTURE_3D, i, gl_internal_format, mip_width, mip_height, mip_depth, 0, gl_format, gl_type, buffer.get()); } else { MipLevelInfo mip_info; MakeMipLevelInfo(&mip_info, format, width, height, depth, i); unsigned int size = GetMipLevelSize(mip_info); glCompressedTexImage3D(GL_TEXTURE_3D, i, gl_internal_format, mip_width, mip_height, mip_depth, 0, size, buffer.get()); } mip_width = std::max(1U, mip_width >> 1); mip_height = std::max(1U, mip_height >> 1); mip_depth = std::max(1U, mip_depth >> 1); } return new Texture3DGL(levels, format, enable_render_surfaces, flags, width, height, depth, gl_texture); } bool Texture3DGL::SetData(const Volume& volume, unsigned int level, texture::Face face, unsigned int row_pitch, unsigned int slice_pitch, unsigned int size, const void *data) { if (level >= levels()) return false; MipLevelInfo mip_info; MakeMipLevelInfo(&mip_info, format(), width_, height_, depth_, level); SetImageHelper helper; if (!helper.Initialize(mip_info, volume, row_pitch, slice_pitch, size, data)) return false; GLenum gl_internal_format = 0; GLenum gl_format = 0; GLenum gl_type = 0; bool r = GetGLFormatType(format(), &gl_internal_format, &gl_format, &gl_type); DCHECK(r); glBindTexture(GL_TEXTURE_3D, gl_texture_); if (gl_format) { glTexSubImage3D(GL_TEXTURE_3D, level, volume.x, volume.y, volume.z, volume.width, volume.height, volume.depth, gl_format, gl_type, helper.image_data()); } else { glCompressedTexSubImage3D(GL_TEXTURE_3D, level, volume.x, volume.y, volume.z, volume.width, volume.height, volume.depth, gl_internal_format, helper.image_size(), helper.image_data()); } return true; } bool Texture3DGL::GetData(const Volume& volume, unsigned int level, texture::Face face, unsigned int row_pitch, unsigned int slice_pitch, unsigned int size, void *data) { if (level >= levels()) return false; MipLevelInfo mip_info; MakeMipLevelInfo(&mip_info, format(), width_, height_, depth_, level); GetImageHelper helper; if (!helper.Initialize(mip_info, volume, row_pitch, slice_pitch, size, data)) return false; GLenum gl_internal_format = 0; GLenum gl_format = 0; GLenum gl_type = 0; bool r = GetGLFormatType(format(), &gl_internal_format, &gl_format, &gl_type); DCHECK(r); glBindTexture(GL_TEXTURE_3D, gl_texture_); if (gl_format) { glGetTexImage(GL_TEXTURE_3D, level, gl_format, gl_type, helper.image_data()); } else { glGetCompressedTexImage(GL_TEXTURE_3D, level, helper.image_data()); } helper.Finalize(); return true; } bool Texture3DGL::CreateRenderSurface(int width, int height, int mip_level, int side) { return false; } bool Texture3DGL::InstallFrameBufferObjects( RenderSurfaceGL *gl_surface) { return false; } TextureCubeGL *TextureCubeGL::Create(unsigned int side, unsigned int levels, texture::Format format, unsigned int flags, bool enable_render_surfaces) { DCHECK_GT(side, 0U); DCHECK_GT(levels, 0U); GLenum gl_internal_format = 0; GLenum gl_format = 0; GLenum gl_type = 0; bool r = GetGLFormatType(format, &gl_internal_format, &gl_format, &gl_type); DCHECK(r); // Was checked in the decoder. GLuint gl_texture = 0; glGenTextures(1, &gl_texture); glBindTexture(GL_TEXTURE_CUBE_MAP, gl_texture); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, levels-1); // glCompressedTexImage2D does't accept NULL as a parameter, so we need // to pass in some data. scoped_array<unsigned char> buffer; MipLevelInfo mip_info; MakeMipLevelInfo(&mip_info, format, side, side, 1, 0); unsigned int size = GetMipLevelSize(mip_info); buffer.reset(new unsigned char[size]); memset(buffer.get(), 0, size); unsigned int mip_side = side; for (unsigned int i = 0; i < levels; ++i) { if (gl_format) { for (unsigned int face = 0; face < 6; ++face) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, i, gl_internal_format, mip_side, mip_side, 0, gl_format, gl_type, buffer.get()); } } else { MipLevelInfo mip_info; MakeMipLevelInfo(&mip_info, format, side, side, 1, i); unsigned int size = GetMipLevelSize(mip_info); for (unsigned int face = 0; face < 6; ++face) { glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, i, gl_internal_format, mip_side, mip_side, 0, size, buffer.get()); } } mip_side = std::max(1U, mip_side >> 1); } return new TextureCubeGL( levels, format, enable_render_surfaces, flags, side, gl_texture); } // Check that GL_TEXTURE_CUBE_MAP_POSITIVE_X + face yields the correct GLenum. COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::kFacePositiveX == GL_TEXTURE_CUBE_MAP_POSITIVE_X, POSITIVE_X_ENUMS_DO_NOT_MATCH); COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::kFaceNegativeX == GL_TEXTURE_CUBE_MAP_NEGATIVE_X, NEGATIVE_X_ENUMS_DO_NOT_MATCH); COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::kFacePositiveY == GL_TEXTURE_CUBE_MAP_POSITIVE_Y, POSITIVE_Y_ENUMS_DO_NOT_MATCH); COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::kFaceNegativeY == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, NEGATIVE_Y_ENUMS_DO_NOT_MATCH); COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::kFacePositiveZ == GL_TEXTURE_CUBE_MAP_POSITIVE_Z, POSITIVE_Z_ENUMS_DO_NOT_MATCH); COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::kFaceNegativeZ == GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, NEGATIVE_Z_ENUMS_DO_NOT_MATCH); bool TextureCubeGL::SetData(const Volume& volume, unsigned int level, texture::Face face, unsigned int row_pitch, unsigned int slice_pitch, unsigned int size, const void *data) { if (level >= levels()) return false; MipLevelInfo mip_info; MakeMipLevelInfo(&mip_info, format(), side_, side_, 1, level); SetImageHelper helper; if (!helper.Initialize(mip_info, volume, row_pitch, slice_pitch, size, data)) return false; GLenum gl_internal_format = 0; GLenum gl_format = 0; GLenum gl_type = 0; bool r = GetGLFormatType(format(), &gl_internal_format, &gl_format, &gl_type); DCHECK(r); glBindTexture(GL_TEXTURE_CUBE_MAP, gl_texture_); GLenum face_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face; if (gl_format) { glTexSubImage2D(face_target, level, volume.x, volume.y, volume.width, volume.height, gl_format, gl_type, helper.image_data()); } else { glCompressedTexSubImage2D(face_target, level, volume.x, volume.y, volume.width, volume.height, gl_internal_format, helper.image_size(), helper.image_data()); } return true; } bool TextureCubeGL::GetData(const Volume& volume, unsigned int level, texture::Face face, unsigned int row_pitch, unsigned int slice_pitch, unsigned int size, void *data) { if (level >= levels()) return false; MipLevelInfo mip_info; MakeMipLevelInfo(&mip_info, format(), side_, side_, 1, level); GetImageHelper helper; if (!helper.Initialize(mip_info, volume, row_pitch, slice_pitch, size, data)) return false; GLenum gl_internal_format = 0; GLenum gl_format = 0; GLenum gl_type = 0; bool r = GetGLFormatType(format(), &gl_internal_format, &gl_format, &gl_type); DCHECK(r); glBindTexture(GL_TEXTURE_CUBE_MAP, gl_texture_); GLenum face_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face; if (gl_format) { glGetTexImage(face_target, level, gl_format, gl_type, helper.image_data()); } else { glGetCompressedTexImage(face_target, level, helper.image_data()); } helper.Finalize(); return true; } bool TextureCubeGL::CreateRenderSurface(int width, int height, int mip_level, int side) { return false; } bool TextureCubeGL::InstallFrameBufferObjects( RenderSurfaceGL *gl_surface) { ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, gl_surface->side(), gl_texture_, gl_surface->mip_level()); GLenum framebuffer_status = ::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (GL_FRAMEBUFFER_COMPLETE_EXT != framebuffer_status) { return false; } return true; } // Destroys a texture resource. parse_error::ParseError GAPIGL::DestroyTexture(ResourceId id) { // Dirty effect, because this texture id may be used. DirtyEffect(); return textures_.Destroy(id) ? parse_error::kParseNoError : parse_error::kParseInvalidArguments; } // Creates a 2D texture resource. parse_error::ParseError GAPIGL::CreateTexture2D( ResourceId id, unsigned int width, unsigned int height, unsigned int levels, texture::Format format, unsigned int flags, bool enable_render_surfaces) { Texture2DGL *texture = Texture2DGL::Create( width, height, levels, format, flags, enable_render_surfaces); if (!texture) return parse_error::kParseInvalidArguments; // Dirty effect, because this texture id may be used. DirtyEffect(); textures_.Assign(id, texture); return parse_error::kParseNoError; } // Creates a 3D texture resource. parse_error::ParseError GAPIGL::CreateTexture3D( ResourceId id, unsigned int width, unsigned int height, unsigned int depth, unsigned int levels, texture::Format format, unsigned int flags, bool enable_render_surfaces) { Texture3DGL *texture = Texture3DGL::Create( width, height, depth, levels, format, flags, enable_render_surfaces); if (!texture) return parse_error::kParseInvalidArguments; // Dirty effect, because this texture id may be used. DirtyEffect(); textures_.Assign(id, texture); return parse_error::kParseNoError; } // Creates a cube map texture resource. parse_error::ParseError GAPIGL::CreateTextureCube( ResourceId id, unsigned int side, unsigned int levels, texture::Format format, unsigned int flags, bool enable_render_surfaces) { TextureCubeGL *texture = TextureCubeGL::Create( side, levels, format, flags, enable_render_surfaces); if (!texture) return parse_error::kParseInvalidArguments; // Dirty effect, because this texture id may be used. DirtyEffect(); textures_.Assign(id, texture); return parse_error::kParseNoError; } // Copies the data into a texture resource. parse_error::ParseError GAPIGL::SetTextureData( ResourceId id, unsigned int x, unsigned int y, unsigned int z, unsigned int width, unsigned int height, unsigned int depth, unsigned int level, texture::Face face, unsigned int row_pitch, unsigned int slice_pitch, unsigned int size, const void *data) { TextureGL *texture = textures_.Get(id); if (!texture) return parse_error::kParseInvalidArguments; Volume volume = {x, y, z, width, height, depth}; // Dirty effect: SetData may need to call glBindTexture which will mess up the // sampler parameters. DirtyEffect(); return texture->SetData(volume, level, face, row_pitch, slice_pitch, size, data) ? parse_error::kParseNoError : parse_error::kParseInvalidArguments; } // Copies the data from a texture resource. parse_error::ParseError GAPIGL::GetTextureData( ResourceId id, unsigned int x, unsigned int y, unsigned int z, unsigned int width, unsigned int height, unsigned int depth, unsigned int level, texture::Face face, unsigned int row_pitch, unsigned int slice_pitch, unsigned int size, void *data) { TextureGL *texture = textures_.Get(id); if (!texture) return parse_error::kParseInvalidArguments; Volume volume = {x, y, z, width, height, depth}; // Dirty effect: GetData may need to call glBindTexture which will mess up the // sampler parameters. DirtyEffect(); return texture->GetData(volume, level, face, row_pitch, slice_pitch, size, data) ? parse_error::kParseNoError : parse_error::kParseInvalidArguments; } } // namespace o3d } // namespace command_buffer