diff options
author | 2016-02-26 13:20:48 -0800 | |
---|---|---|
committer | 2016-02-26 13:20:48 -0800 | |
commit | 55f2d2d57f4dd4109aa0c9dab6023373e3b928ec (patch) | |
tree | 6254a42add2b1442385ed153c6d1382403392287 /src/gpu/gl | |
parent | e015c2646f7f553ff10cd6b9783a3f4ededb63bf (diff) |
Creating functions for uploading a mipmapped texture.
BUG=476416
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1249543003
Review URL: https://codereview.chromium.org/1249543003
Diffstat (limited to 'src/gpu/gl')
-rw-r--r-- | src/gpu/gl/GrGLDefines.h | 4 | ||||
-rw-r--r-- | src/gpu/gl/GrGLGpu.cpp | 562 | ||||
-rw-r--r-- | src/gpu/gl/GrGLGpu.h | 24 | ||||
-rw-r--r-- | src/gpu/gl/GrGLTexture.cpp | 12 | ||||
-rw-r--r-- | src/gpu/gl/GrGLTexture.h | 2 |
5 files changed, 414 insertions, 190 deletions
diff --git a/src/gpu/gl/GrGLDefines.h b/src/gpu/gl/GrGLDefines.h index 219501c1f8..a8e57a5b0a 100644 --- a/src/gpu/gl/GrGLDefines.h +++ b/src/gpu/gl/GrGLDefines.h @@ -212,6 +212,10 @@ #define GR_GL_PACK_ALIGNMENT 0x0D05 #define GR_GL_PACK_REVERSE_ROW_ORDER 0x93A4 #define GR_GL_MAX_TEXTURE_SIZE 0x0D33 +#define GR_GL_TEXTURE_MIN_LOD 0x813A +#define GR_GL_TEXTURE_MAX_LOD 0x813B +#define GR_GL_TEXTURE_BASE_LEVEL 0x813C +#define GR_GL_TEXTURE_MAX_LEVEL 0x813D #define GR_GL_MAX_VIEWPORT_DIMS 0x0D3A #define GR_GL_SUBPIXEL_BITS 0x0D50 #define GR_GL_RED_BITS 0x0D52 diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp index 8e6fbadc7b..071b11383d 100644 --- a/src/gpu/gl/GrGLGpu.cpp +++ b/src/gpu/gl/GrGLGpu.cpp @@ -21,8 +21,11 @@ #include "glsl/GrGLSL.h" #include "glsl/GrGLSLCaps.h" #include "glsl/GrGLSLPLSPathRendering.h" +#include "SkMipMap.h" +#include "SkPixmap.h" #include "SkStrokeRec.h" #include "SkTemplates.h" +#include "SkTypes.h" #define GL_CALL(X) GR_GL_CALL(this->glInterface(), X) #define GL_CALL_RET(RET, X) GR_GL_CALL_RET(this->glInterface(), RET, X) @@ -555,7 +558,10 @@ GrTexture* GrGLGpu::onWrapBackendTexture(const GrBackendTextureDesc& desc, #ifdef SK_IGNORE_GL_TEXTURE_TARGET idDesc.fInfo.fID = static_cast<GrGLuint>(desc.fTextureHandle); - // We only support GL_TEXTURE_2D at the moment. + // When we create the texture, we only + // create GL_TEXTURE_2D at the moment. + // External clients can do something different. + idDesc.fInfo.fTarget = GR_GL_TEXTURE_2D; #else idDesc.fInfo = *info; @@ -809,8 +815,8 @@ static bool check_write_and_transfer_input(GrGLTexture* glTex, GrSurface* surfac bool GrGLGpu::onWritePixels(GrSurface* surface, int left, int top, int width, int height, - GrPixelConfig config, const void* buffer, - size_t rowBytes) { + GrPixelConfig config, + const SkTArray<GrMipLevel>& texels) { GrGLTexture* glTex = static_cast<GrGLTexture*>(surface->asTexture()); if (!check_write_and_transfer_input(glTex, surface, config)) { @@ -824,19 +830,14 @@ bool GrGLGpu::onWritePixels(GrSurface* surface, if (GrPixelConfigIsCompressed(glTex->desc().fConfig)) { // We check that config == desc.fConfig in GrGLGpu::canWriteTexturePixels() SkASSERT(config == glTex->desc().fConfig); - success = this->uploadCompressedTexData(glTex->desc(), glTex->target(), buffer, + success = this->uploadCompressedTexData(glTex->desc(), glTex->target(), texels, kWrite_UploadType, left, top, width, height); } else { success = this->uploadTexData(glTex->desc(), glTex->target(), kWrite_UploadType, - left, top, width, height, config, buffer, rowBytes); - } - - if (success) { - glTex->texturePriv().dirtyMipMaps(true); - return true; + left, top, width, height, config, texels); } - return false; + return success; } bool GrGLGpu::onTransferPixels(GrSurface* surface, @@ -865,9 +866,13 @@ bool GrGLGpu::onTransferPixels(GrSurface* surface, GL_CALL(BindBuffer(glBuffer->bufferType(), glBuffer->bufferID())); bool success = false; + GrMipLevel mipLevel; + mipLevel.fPixels = buffer; + mipLevel.fRowBytes = rowBytes; + SkSTArray<1, GrMipLevel> texels; + texels.push_back(mipLevel); success = this->uploadTexData(glTex->desc(), glTex->target(), kTransfer_UploadType, - left, top, width, height, config, buffer, rowBytes); - + left, top, width, height, config, texels); if (success) { glTex->texturePriv().dirtyMipMaps(true); return true; @@ -906,30 +911,178 @@ static inline GrGLenum check_alloc_error(const GrSurfaceDesc& desc, } } +/** + * Creates storage space for the texture and fills it with texels. + * + * @param desc The surface descriptor for the texture being created. + * @param interface The GL interface in use. + * @param target The GL target to which the texture is bound + * @param internalFormat The data format used for the internal storage of the texture. + * @param externalFormat The data format used for the external storage of the texture. + * @param externalType The type of the data used for the external storage of the texture. + * @param texels The texel data of the texture being created. + * @param baseWidth The width of the texture's base mipmap level + * @param baseHeight The height of the texture's base mipmap level + * @param succeeded Set to true if allocating and populating the texture completed + * without error. + */ +static void allocate_and_populate_uncompressed_texture(const GrSurfaceDesc& desc, + const GrGLInterface& interface, + GrGLenum target, + GrGLenum internalFormat, + GrGLenum externalFormat, + GrGLenum externalType, + const SkTArray<GrMipLevel>& texels, + int baseWidth, int baseHeight, + bool* succeeded) { + CLEAR_ERROR_BEFORE_ALLOC(&interface); + *succeeded = true; + for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) { + int twoToTheMipLevel = 1 << currentMipLevel; + int currentWidth = SkTMax(1, baseWidth / twoToTheMipLevel); + int currentHeight = SkTMax(1, baseHeight / twoToTheMipLevel); + const void* currentMipData = texels[currentMipLevel].fPixels; + // Even if curremtMipData is nullptr, continue to call TexImage2D. + // This will allocate texture memory which we can later populate. + GL_ALLOC_CALL(&interface, + TexImage2D(target, + currentMipLevel, + internalFormat, + currentWidth, + currentHeight, + 0, // border + externalFormat, externalType, + currentMipData)); + GrGLenum error = check_alloc_error(desc, &interface); + if (error != GR_GL_NO_ERROR) { + *succeeded = false; + break; + } + } +} + +/** + * Creates storage space for the texture and fills it with texels. + * + * @param desc The surface descriptor for the texture being created. + * @param interface The GL interface in use. + * @param target The GL target to which the texture is bound + * @param internalFormat The data format used for the internal storage of the texture. + * @param texels The texel data of the texture being created. + * @param baseWidth The width of the texture's base mipmap level + * @param baseHeight The height of the texture's base mipmap level + */ +static bool allocate_and_populate_compressed_texture(const GrSurfaceDesc& desc, + const GrGLInterface& interface, + GrGLenum target, GrGLenum internalFormat, + const SkTArray<GrMipLevel>& texels, + int baseWidth, int baseHeight) { + CLEAR_ERROR_BEFORE_ALLOC(&interface); + for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) { + int twoToTheMipLevel = 1 << currentMipLevel; + int currentWidth = SkTMax(1, baseWidth / twoToTheMipLevel); + int currentHeight = SkTMax(1, baseHeight / twoToTheMipLevel); + + // Make sure that the width and height that we pass to OpenGL + // is a multiple of the block size. + size_t dataSize = GrCompressedFormatDataSize(desc.fConfig, baseWidth, baseHeight); + + GL_ALLOC_CALL(&interface, + CompressedTexImage2D(target, + currentMipLevel, + internalFormat, + currentWidth, + currentHeight, + 0, // border + SkToInt(dataSize), + texels[currentMipLevel].fPixels)); + + GrGLenum error = check_alloc_error(desc, &interface); + if (error != GR_GL_NO_ERROR) { + return false; + } + } + + return true; +} + +/** + * After a texture is created, any state which was altered during its creation + * needs to be restored. + * + * @param interface The GL interface to use. + * @param caps The capabilities of the GL device. + * @param restoreGLRowLength Should the row length unpacking be restored? + * @param glFlipY Did GL flip the texture vertically? + */ +static void restore_pixelstore_state(const GrGLInterface& interface, const GrGLCaps& caps, + bool restoreGLRowLength, bool glFlipY) { + if (restoreGLRowLength) { + SkASSERT(caps.unpackRowLengthSupport()); + GR_GL_CALL(&interface, PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0)); + } + if (glFlipY) { + GR_GL_CALL(&interface, PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_FALSE)); + } +} + bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, GrGLenum target, UploadType uploadType, int left, int top, int width, int height, GrPixelConfig dataConfig, - const void* dataOrOffset, - size_t rowBytes) { - SkASSERT(dataOrOffset || kNewTexture_UploadType == uploadType || - kTransfer_UploadType == uploadType); - + const SkTArray<GrMipLevel>& texels) { // If we're uploading compressed data then we should be using uploadCompressedTexData SkASSERT(!GrPixelConfigIsCompressed(dataConfig)); SkASSERT(this->caps()->isConfigTexturable(desc.fConfig)); + // texels is const. + // But we may need to flip the texture vertically to prepare it. + // Rather than flip in place and alter the incoming data, + // we allocate a new buffer to flip into. + // This means we need to make a non-const shallow copy of texels. + SkTArray<GrMipLevel> texelsShallowCopy(texels); + + for (int currentMipLevel = texelsShallowCopy.count() - 1; currentMipLevel >= 0; + currentMipLevel--) { + SkASSERT(texelsShallowCopy[currentMipLevel].fPixels || + kNewTexture_UploadType == uploadType || kTransfer_UploadType == uploadType); + } + + const GrGLInterface* interface = this->glInterface(); + const GrGLCaps& caps = this->glCaps(); + size_t bpp = GrBytesPerPixel(dataConfig); - if (!GrSurfacePriv::AdjustWritePixelParams(desc.fWidth, desc.fHeight, bpp, &left, &top, - &width, &height, &dataOrOffset, &rowBytes)) { + + if (width == 0 || height == 0) { return false; } - size_t trimRowBytes = width * bpp; - // in case we need a temporary, trimmed copy of the src pixels - SkAutoSMalloc<128 * 128> tempStorage; + for (int currentMipLevel = 0; currentMipLevel < texelsShallowCopy.count(); currentMipLevel++) { + int twoToTheMipLevel = 1 << currentMipLevel; + int currentWidth = SkTMax(1, width / twoToTheMipLevel); + int currentHeight = SkTMax(1, height / twoToTheMipLevel); + + if (texelsShallowCopy[currentMipLevel].fPixels == nullptr) { + continue; + } + + if (currentHeight > SK_MaxS32 || + currentWidth > SK_MaxS32) { + return false; + } + if (!GrSurfacePriv::AdjustWritePixelParams(desc.fWidth, desc.fHeight, bpp, &left, &top, + ¤tWidth, + ¤tHeight, + &texelsShallowCopy[currentMipLevel].fPixels, + &texelsShallowCopy[currentMipLevel].fRowBytes)) { + return false; + } + if (currentWidth < 0 || currentHeight < 0) { + return false; + } + } // Internal format comes from the texture desc. GrGLenum internalFormat; @@ -949,31 +1102,67 @@ bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, bool restoreGLRowLength = false; bool swFlipY = false; bool glFlipY = false; - if (dataOrOffset) { - if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) { - if (this->glCaps().unpackFlipYSupport()) { - glFlipY = true; - } else { - swFlipY = true; - } + + if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) { + if (caps.unpackFlipYSupport()) { + glFlipY = true; + } else { + swFlipY = true; + } + } + + // in case we need a temporary, trimmed copy of the src pixels + SkAutoSMalloc<128 * 128> tempStorage; + + // find the combined size of all the mip levels and the relative offset of + // each into the collective buffer + size_t combined_buffer_size = 0; + SkTArray<size_t> individual_mip_offsets(texelsShallowCopy.count()); + for (int currentMipLevel = 0; currentMipLevel < texelsShallowCopy.count(); currentMipLevel++) { + int twoToTheMipLevel = 1 << currentMipLevel; + int currentWidth = SkTMax(1, width / twoToTheMipLevel); + int currentHeight = SkTMax(1, height / twoToTheMipLevel); + const size_t trimmedSize = currentWidth * bpp * currentHeight; + individual_mip_offsets.push_back(combined_buffer_size); + combined_buffer_size += trimmedSize; + } + char* buffer = (char*)tempStorage.reset(combined_buffer_size); + + for (int currentMipLevel = 0; currentMipLevel < texelsShallowCopy.count(); currentMipLevel++) { + if (texelsShallowCopy[currentMipLevel].fPixels == nullptr) { + continue; } - if (this->glCaps().unpackRowLengthSupport() && !swFlipY) { + + int twoToTheMipLevel = 1 << currentMipLevel; + int currentWidth = SkTMax(1, width / twoToTheMipLevel); + int currentHeight = SkTMax(1, height / twoToTheMipLevel); + const size_t trimRowBytes = currentWidth * bpp; + + /* + * check whether to allocate a temporary buffer for flipping y or + * because our srcData has extra bytes past each row. If so, we need + * to trim those off here, since GL ES may not let us specify + * GL_UNPACK_ROW_LENGTH. + */ + restoreGLRowLength = false; + + const size_t rowBytes = texelsShallowCopy[currentMipLevel].fRowBytes; + if (caps.unpackRowLengthSupport() && !swFlipY) { // can't use this for flipping, only non-neg values allowed. :( if (rowBytes != trimRowBytes) { GrGLint rowLength = static_cast<GrGLint>(rowBytes / bpp); - GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, rowLength)); + GR_GL_CALL(interface, PixelStorei(GR_GL_UNPACK_ROW_LENGTH, rowLength)); restoreGLRowLength = true; } } else if (kTransfer_UploadType != uploadType) { if (trimRowBytes != rowBytes || swFlipY) { // copy data into our new storage, skipping the trailing bytes - size_t trimSize = height * trimRowBytes; - const char* src = (const char*)dataOrOffset; - if (swFlipY) { - src += (height - 1) * rowBytes; + const char* src = (const char*)texelsShallowCopy[currentMipLevel].fPixels; + if (swFlipY && currentHeight >= 1) { + src += (currentHeight - 1) * rowBytes; } - char* dst = (char*)tempStorage.reset(trimSize); - for (int y = 0; y < height; y++) { + char* dst = buffer + individual_mip_offsets[currentMipLevel]; + for (int y = 0; y < currentHeight; y++) { memcpy(dst, src, trimRowBytes); if (swFlipY) { src -= rowBytes; @@ -983,59 +1172,54 @@ bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, dst += trimRowBytes; } // now point data to our copied version - dataOrOffset = tempStorage.get(); + texelsShallowCopy[currentMipLevel].fPixels = buffer + + individual_mip_offsets[currentMipLevel]; + texelsShallowCopy[currentMipLevel].fRowBytes = trimRowBytes; } } else { return false; } if (glFlipY) { - GL_CALL(PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_TRUE)); + GR_GL_CALL(interface, PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_TRUE)); } - GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, config_alignment(dataConfig))); + GR_GL_CALL(interface, PixelStorei(GR_GL_UNPACK_ALIGNMENT, + config_alignment(desc.fConfig))); } + bool succeeded = true; - if (kNewTexture_UploadType == uploadType) { - if (dataOrOffset && - !(0 == left && 0 == top && desc.fWidth == width && desc.fHeight == height)) { - succeeded = false; - } else { - if (desc.fTextureStorageAllocator.fAllocateTextureStorage) { - if (dataOrOffset) { - GL_CALL(TexSubImage2D(target, - 0, // level - left, top, - width, height, - externalFormat, externalType, dataOrOffset)); - } - } else { - CLEAR_ERROR_BEFORE_ALLOC(this->glInterface()); - GL_ALLOC_CALL(this->glInterface(), TexImage2D( - target, 0, internalFormat, desc.fWidth, desc.fHeight, 0, externalFormat, - externalType, dataOrOffset)); - GrGLenum error = check_alloc_error(desc, this->glInterface()); - if (error != GR_GL_NO_ERROR) { - succeeded = false; - } - } - } + if (kNewTexture_UploadType == uploadType && + 0 == left && 0 == top && + desc.fWidth == width && desc.fHeight == height && + !desc.fTextureStorageAllocator.fAllocateTextureStorage) { + allocate_and_populate_uncompressed_texture(desc, *interface, target, + internalFormat, externalFormat, + externalType, texelsShallowCopy, + width, height, &succeeded); } else { if (swFlipY || glFlipY) { top = desc.fHeight - (top + height); } - GL_CALL(TexSubImage2D(target, - 0, // level - left, top, - width, height, - externalFormat, externalType, dataOrOffset)); - } + for (int currentMipLevel = 0; currentMipLevel < texelsShallowCopy.count(); + currentMipLevel++) { + int twoToTheMipLevel = 1 << currentMipLevel; + int currentWidth = SkTMax(1, width / twoToTheMipLevel); + int currentHeight = SkTMax(1, height / twoToTheMipLevel); + if (texelsShallowCopy[currentMipLevel].fPixels == nullptr) { + continue; + } - if (restoreGLRowLength) { - SkASSERT(this->glCaps().unpackRowLengthSupport()); - GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0)); - } - if (glFlipY) { - GL_CALL(PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_FALSE)); + GL_CALL(TexSubImage2D(target, + currentMipLevel, + left, top, + currentWidth, + currentHeight, + externalFormat, externalType, + texelsShallowCopy[currentMipLevel].fPixels)); + } } + + restore_pixelstore_state(*interface, caps, restoreGLRowLength, glFlipY); + return succeeded; } @@ -1046,16 +1230,19 @@ bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, // see fit if they want to go against the "standard" way to do it. bool GrGLGpu::uploadCompressedTexData(const GrSurfaceDesc& desc, GrGLenum target, - const void* data, + const SkTArray<GrMipLevel>& texels, UploadType uploadType, int left, int top, int width, int height) { SkASSERT(this->caps()->isConfigTexturable(desc.fConfig)); - SkASSERT(kTransfer_UploadType != uploadType && - (data || kNewTexture_UploadType != uploadType)); + SkASSERT(kTransfer_UploadType != uploadType && + (texels[0].fPixels || kNewTexture_UploadType != uploadType)); // No support for software flip y, yet... SkASSERT(kBottomLeft_GrSurfaceOrigin != desc.fOrigin); + const GrGLInterface* interface = this->glInterface(); + const GrGLCaps& caps = this->glCaps(); + if (-1 == width) { width = desc.fWidth; } @@ -1074,42 +1261,42 @@ bool GrGLGpu::uploadCompressedTexData(const GrSurfaceDesc& desc, } #endif - // Make sure that the width and height that we pass to OpenGL - // is a multiple of the block size. - size_t dataSize = GrCompressedFormatDataSize(desc.fConfig, width, height); - // We only need the internal format for compressed 2D textures. GrGLenum internalFormat; - if (!this->glCaps().getCompressedTexImageFormats(desc.fConfig, &internalFormat)) { + if (!caps.getCompressedTexImageFormats(desc.fConfig, &internalFormat)) { return false; } if (kNewTexture_UploadType == uploadType) { - CLEAR_ERROR_BEFORE_ALLOC(this->glInterface()); - GL_ALLOC_CALL(this->glInterface(), - CompressedTexImage2D(target, - 0, // level - internalFormat, - width, height, - 0, // border - SkToInt(dataSize), - data)); - GrGLenum error = check_alloc_error(desc, this->glInterface()); - if (error != GR_GL_NO_ERROR) { - return false; - } + return allocate_and_populate_compressed_texture(desc, *interface, target, internalFormat, + texels, width, height); } else { // Paletted textures can't be updated. if (GR_GL_PALETTE8_RGBA8 == internalFormat) { return false; } - GL_CALL(CompressedTexSubImage2D(target, - 0, // level - left, top, - width, height, - internalFormat, - SkToInt(dataSize), - data)); + for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) { + if (texels[currentMipLevel].fPixels == nullptr) { + continue; + } + + int twoToTheMipLevel = 1 << currentMipLevel; + int currentWidth = SkTMax(1, width / twoToTheMipLevel); + int currentHeight = SkTMax(1, height / twoToTheMipLevel); + + // Make sure that the width and height that we pass to OpenGL + // is a multiple of the block size. + size_t dataSize = GrCompressedFormatDataSize(desc.fConfig, currentWidth, + currentHeight); + GL_CALL(CompressedTexSubImage2D(target, + currentMipLevel, + left, top, + currentWidth, + currentHeight, + internalFormat, + SkToInt(dataSize), + texels[currentMipLevel].fPixels)); + } } return true; @@ -1273,9 +1460,48 @@ static size_t as_size_t(int x) { } #endif +static GrGLTexture::IDDesc generate_gl_texture(const GrGLInterface* interface, + GrGpuResource::LifeCycle lifeCycle) { + GrGLTexture::IDDesc idDesc; + idDesc.fInfo.fID = 0; + GR_GL_CALL(interface, GenTextures(1, &idDesc.fInfo.fID)); + idDesc.fLifeCycle = lifeCycle; + // When we create the texture, we only + // create GL_TEXTURE_2D at the moment. + // External clients can do something different. + idDesc.fInfo.fTarget = GR_GL_TEXTURE_2D; + return idDesc; +} + +static void set_initial_texture_params(const GrGLInterface* interface, + const GrGLTextureInfo& info, + GrGLTexture::TexParams* initialTexParams) { + // Some drivers like to know filter/wrap before seeing glTexImage2D. Some + // drivers have a bug where an FBO won't be complete if it includes a + // texture that is not mipmap complete (considering the filter in use). + // we only set a subset here so invalidate first + initialTexParams->invalidate(); + initialTexParams->fMinFilter = GR_GL_NEAREST; + initialTexParams->fMagFilter = GR_GL_NEAREST; + initialTexParams->fWrapS = GR_GL_CLAMP_TO_EDGE; + initialTexParams->fWrapT = GR_GL_CLAMP_TO_EDGE; + GR_GL_CALL(interface, TexParameteri(info.fTarget, + GR_GL_TEXTURE_MAG_FILTER, + initialTexParams->fMagFilter)); + GR_GL_CALL(interface, TexParameteri(info.fTarget, + GR_GL_TEXTURE_MIN_FILTER, + initialTexParams->fMinFilter)); + GR_GL_CALL(interface, TexParameteri(info.fTarget, + GR_GL_TEXTURE_WRAP_S, + initialTexParams->fWrapS)); + GR_GL_CALL(interface, TexParameteri(info.fTarget, + GR_GL_TEXTURE_WRAP_T, + initialTexParams->fWrapT)); +} + GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc, GrGpuResource::LifeCycle lifeCycle, - const void* srcData, size_t rowBytes) { + const SkTArray<GrMipLevel>& texels) { // We fail if the MSAA was requested and is not available. if (GrGLCaps::kNone_MSFBOType == this->glCaps().msFBOType() && desc.fSampleCnt) { //SkDebugf("MSAA RT requested but not supported on this platform."); @@ -1287,8 +1513,7 @@ GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc, GrGLTexture::IDDesc idDesc; idDesc.fLifeCycle = lifeCycle; GrGLTexture::TexParams initialTexParams; - if (!this->createTextureImpl(desc, &idDesc.fInfo, renderTarget, srcData, - &initialTexParams, rowBytes)) { + if (!this->createTextureImpl(desc, &idDesc.fInfo, renderTarget, &initialTexParams, texels)) { return return_null_texture(); } @@ -1304,31 +1529,29 @@ GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc, } tex = new GrGLTextureRenderTarget(this, desc, idDesc, rtIDDesc); } else { - tex = new GrGLTexture(this, desc, idDesc); + bool wasMipMapDataProvided = false; + if (texels.count() > 1) { + wasMipMapDataProvided = true; + } + tex = new GrGLTexture(this, desc, idDesc, wasMipMapDataProvided); } tex->setCachedTexParams(initialTexParams, this->getResetTimestamp()); #ifdef TRACE_TEXTURE_CREATION SkDebugf("--- new texture [%d] size=(%d %d) config=%d\n", - glTexDesc.fTextureID, desc.fWidth, desc.fHeight, desc.fConfig); + glTexDesc.fInfo.fID, desc.fWidth, desc.fHeight, desc.fConfig); #endif return tex; } GrTexture* GrGLGpu::onCreateCompressedTexture(const GrSurfaceDesc& desc, GrGpuResource::LifeCycle lifeCycle, - const void* srcData) { + const SkTArray<GrMipLevel>& texels) { // Make sure that we're not flipping Y. if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) { return return_null_texture(); } - GrGLTexture::IDDesc idDesc; - idDesc.fInfo.fID = 0; - GL_CALL(GenTextures(1, &idDesc.fInfo.fID)); - idDesc.fLifeCycle = lifeCycle; - // We only support GL_TEXTURE_2D at the moment. - idDesc.fInfo.fTarget = GR_GL_TEXTURE_2D; - + GrGLTexture::IDDesc idDesc = generate_gl_texture(this->glInterface(), lifeCycle); if (!idDesc.fInfo.fID) { return return_null_texture(); } @@ -1336,30 +1559,10 @@ GrTexture* GrGLGpu::onCreateCompressedTexture(const GrSurfaceDesc& desc, this->setScratchTextureUnit(); GL_CALL(BindTexture(idDesc.fInfo.fTarget, idDesc.fInfo.fID)); - // Some drivers like to know filter/wrap before seeing glTexImage2D. Some - // drivers have a bug where an FBO won't be complete if it includes a - // texture that is not mipmap complete (considering the filter in use). GrGLTexture::TexParams initialTexParams; - // we only set a subset here so invalidate first - initialTexParams.invalidate(); - initialTexParams.fMinFilter = GR_GL_NEAREST; - initialTexParams.fMagFilter = GR_GL_NEAREST; - initialTexParams.fWrapS = GR_GL_CLAMP_TO_EDGE; - initialTexParams.fWrapT = GR_GL_CLAMP_TO_EDGE; - GL_CALL(TexParameteri(idDesc.fInfo.fTarget, - GR_GL_TEXTURE_MAG_FILTER, - initialTexParams.fMagFilter)); - GL_CALL(TexParameteri(idDesc.fInfo.fTarget, - GR_GL_TEXTURE_MIN_FILTER, - initialTexParams.fMinFilter)); - GL_CALL(TexParameteri(idDesc.fInfo.fTarget, - GR_GL_TEXTURE_WRAP_S, - initialTexParams.fWrapS)); - GL_CALL(TexParameteri(idDesc.fInfo.fTarget, - GR_GL_TEXTURE_WRAP_T, - initialTexParams.fWrapT)); - - if (!this->uploadCompressedTexData(desc, idDesc.fInfo.fTarget, srcData)) { + set_initial_texture_params(this->glInterface(), idDesc.fInfo, &initialTexParams); + + if (!this->uploadCompressedTexData(desc, idDesc.fInfo.fTarget, texels)) { GL_CALL(DeleteTextures(1, &idDesc.fInfo.fID)); return return_null_texture(); } @@ -1369,7 +1572,7 @@ GrTexture* GrGLGpu::onCreateCompressedTexture(const GrSurfaceDesc& desc, tex->setCachedTexParams(initialTexParams, this->getResetTimestamp()); #ifdef TRACE_TEXTURE_CREATION SkDebugf("--- new compressed texture [%d] size=(%d %d) config=%d\n", - glTexDesc.fTextureID, desc.fWidth, desc.fHeight, desc.fConfig); + glTexDesc.fInfo.fID, desc.fWidth, desc.fHeight, desc.fConfig); #endif return tex; } @@ -1512,21 +1715,10 @@ int GrGLGpu::getCompatibleStencilIndex(GrPixelConfig config) { } bool GrGLGpu::createTextureImpl(const GrSurfaceDesc& desc, GrGLTextureInfo* info, - bool renderTarget, const void* srcData, - GrGLTexture::TexParams* initialTexParams, size_t rowBytes) { - // Some drivers like to know filter/wrap before seeing glTexImage2D. Some - // drivers have a bug where an FBO won't be complete if it includes a - // texture that is not mipmap complete (considering the filter in use). - - // we only set a subset here so invalidate first - initialTexParams->invalidate(); - initialTexParams->fMinFilter = GR_GL_NEAREST; - initialTexParams->fMagFilter = GR_GL_NEAREST; - initialTexParams->fWrapS = GR_GL_CLAMP_TO_EDGE; - initialTexParams->fWrapT = GR_GL_CLAMP_TO_EDGE; - + bool renderTarget, GrGLTexture::TexParams* initialTexParams, + const SkTArray<GrMipLevel>& texels) { if (desc.fTextureStorageAllocator.fAllocateTextureStorage) { - return this->createTextureExternalAllocatorImpl(desc, info, srcData, rowBytes); + return this->createTextureExternalAllocatorImpl(desc, info, texels); } info->fID = 0; @@ -1547,32 +1739,32 @@ bool GrGLGpu::createTextureImpl(const GrSurfaceDesc& desc, GrGLTextureInfo* info GR_GL_FRAMEBUFFER_ATTACHMENT)); } - GL_CALL(TexParameteri(info->fTarget, - GR_GL_TEXTURE_MAG_FILTER, - initialTexParams->fMagFilter)); - GL_CALL(TexParameteri(info->fTarget, - GR_GL_TEXTURE_MIN_FILTER, - initialTexParams->fMinFilter)); - GL_CALL(TexParameteri(info->fTarget, - GR_GL_TEXTURE_WRAP_S, - initialTexParams->fWrapS)); - GL_CALL(TexParameteri(info->fTarget, - GR_GL_TEXTURE_WRAP_T, - initialTexParams->fWrapT)); + if (info) { + set_initial_texture_params(this->glInterface(), *info, initialTexParams); + } if (!this->uploadTexData(desc, info->fTarget, kNewTexture_UploadType, 0, 0, desc.fWidth, desc.fHeight, - desc.fConfig, srcData, rowBytes)) { + desc.fConfig, texels)) { GL_CALL(DeleteTextures(1, &(info->fID))); return false; } return true; } -bool GrGLGpu::createTextureExternalAllocatorImpl( - const GrSurfaceDesc& desc, GrGLTextureInfo* info, const void* srcData, size_t rowBytes) { +bool GrGLGpu::createTextureExternalAllocatorImpl(const GrSurfaceDesc& desc, + GrGLTextureInfo* info, + const SkTArray<GrMipLevel>& texels) { + // We do not make SkTArray available outside of Skia, + // and so we do not want to allow mipmaps to external + // allocators just yet. + SkASSERT(texels.count() == 1); + SkSTArray<1, GrMipLevel> texelsShallowCopy(1); + texelsShallowCopy.push_back(texels[0]); + switch (desc.fTextureStorageAllocator.fAllocateTextureStorage( desc.fTextureStorageAllocator.fCtx, reinterpret_cast<GrBackendObject>(info), - desc.fWidth, desc.fHeight, desc.fConfig, srcData, desc.fOrigin)) { + desc.fWidth, desc.fHeight, desc.fConfig, texelsShallowCopy[0].fPixels, + desc.fOrigin)) { case GrTextureStorageAllocator::Result::kSucceededAndUploaded: return true; case GrTextureStorageAllocator::Result::kFailed: @@ -1583,7 +1775,7 @@ bool GrGLGpu::createTextureExternalAllocatorImpl( if (!this->uploadTexData(desc, info->fTarget, kNewTexture_UploadType, 0, 0, desc.fWidth, desc.fHeight, - desc.fConfig, srcData, rowBytes)) { + desc.fConfig, texelsShallowCopy)) { desc.fTextureStorageAllocator.fDeallocateTextureStorage( desc.fTextureStorageAllocator.fCtx, reinterpret_cast<GrBackendObject>(info)); return false; @@ -3039,12 +3231,17 @@ void GrGLGpu::bindTexture(int unitIdx, const GrTextureParams& params, GrGLTextur newTexParams.fMinFilter = glMinFilterModes[filterMode]; newTexParams.fMagFilter = glMagFilterModes[filterMode]; - if (GrTextureParams::kMipMap_FilterMode == filterMode && - texture->texturePriv().mipMapsAreDirty()) { - GL_CALL(GenerateMipmap(target)); - texture->texturePriv().dirtyMipMaps(false); + if (GrTextureParams::kMipMap_FilterMode == filterMode) { + if (texture->texturePriv().mipMapsAreDirty()) { + GL_CALL(GenerateMipmap(target)); + texture->texturePriv().dirtyMipMaps(false); + texture->texturePriv().setMaxMipMapLevel(SkMipMap::ComputeLevelCount( + texture->width(), texture->height())); + } } + newTexParams.fMaxMipMapLevel = texture->texturePriv().maxMipMapLevel(); + newTexParams.fWrapS = tile_to_gl_wrap(params.getTileModeX()); newTexParams.fWrapT = tile_to_gl_wrap(params.getTileModeY()); get_tex_param_swizzle(texture->config(), this->glCaps(), newTexParams.fSwizzleRGBA); @@ -3056,6 +3253,17 @@ void GrGLGpu::bindTexture(int unitIdx, const GrTextureParams& params, GrGLTextur this->setTextureUnit(unitIdx); GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MIN_FILTER, newTexParams.fMinFilter)); } + if (setAll || newTexParams.fMaxMipMapLevel != oldTexParams.fMaxMipMapLevel) { + if (newTexParams.fMaxMipMapLevel != 0) { + this->setTextureUnit(unitIdx); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MIN_LOD, 0)); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_BASE_LEVEL, 0)); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MAX_LOD, + newTexParams.fMaxMipMapLevel)); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MAX_LEVEL, + newTexParams.fMaxMipMapLevel)); + } + } if (setAll || newTexParams.fWrapS != oldTexParams.fWrapS) { this->setTextureUnit(unitIdx); GL_CALL(TexParameteri(target, GR_GL_TEXTURE_WRAP_S, newTexParams.fWrapS)); diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h index 87c5c3db37..56ffcb3404 100644 --- a/src/gpu/gl/GrGLGpu.h +++ b/src/gpu/gl/GrGLGpu.h @@ -21,7 +21,9 @@ #include "GrGLVertexBuffer.h" #include "GrGpu.h" #include "GrPipelineBuilder.h" +#include "GrTypes.h" #include "GrXferProcessor.h" +#include "SkTArray.h" #include "SkTypes.h" class GrPipeline; @@ -145,10 +147,11 @@ private: void xferBarrier(GrRenderTarget*, GrXferBarrierType) override; GrTexture* onCreateTexture(const GrSurfaceDesc& desc, GrGpuResource::LifeCycle lifeCycle, - const void* srcData, size_t rowBytes) override; + const SkTArray<GrMipLevel>& texels) override; GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc, GrGpuResource::LifeCycle lifeCycle, - const void* srcData) override; + const SkTArray<GrMipLevel>& texels) override; + GrVertexBuffer* onCreateVertexBuffer(size_t size, bool dynamic) override; GrIndexBuffer* onCreateIndexBuffer(size_t size, bool dynamic) override; GrTransferBuffer* onCreateTransferBuffer(size_t size, TransferType type) override; @@ -165,13 +168,13 @@ private: // texture. Otherwise, create the texture directly. // Returns whether the texture is successfully created. On success, the // result is stored in |info|. - // The texture is populated with |srcData|, if it exists. + // The texture is populated with |texels|, if it exists. // The texture parameters are cached in |initialTexParams|. bool createTextureImpl(const GrSurfaceDesc& desc, GrGLTextureInfo* info, - bool renderTarget, const void* srcData, - GrGLTexture::TexParams* initialTexParams, size_t rowBytes); + bool renderTarget, GrGLTexture::TexParams* initialTexParams, + const SkTArray<GrMipLevel>& texels); bool createTextureExternalAllocatorImpl(const GrSurfaceDesc& desc, GrGLTextureInfo* info, - const void* srcData, size_t rowBytes); + const SkTArray<GrMipLevel>& texels); void onClear(GrRenderTarget*, const SkIRect& rect, GrColor color) override; @@ -203,8 +206,8 @@ private: bool onWritePixels(GrSurface*, int left, int top, int width, int height, - GrPixelConfig config, const void* buffer, - size_t rowBytes) override; + GrPixelConfig config, + const SkTArray<GrMipLevel>& texels) override; bool onTransferPixels(GrSurface*, int left, int top, int width, int height, @@ -344,8 +347,7 @@ private: UploadType uploadType, int left, int top, int width, int height, GrPixelConfig dataConfig, - const void* data, - size_t rowBytes); + const SkTArray<GrMipLevel>& texels); // helper for onCreateCompressedTexture. If width and height are // set to -1, then this function will use desc.fWidth and desc.fHeight @@ -355,7 +357,7 @@ private: // with new data. bool uploadCompressedTexData(const GrSurfaceDesc& desc, GrGLenum target, - const void* data, + const SkTArray<GrMipLevel>& texels, UploadType uploadType = kNewTexture_UploadType, int left = 0, int top = 0, int width = -1, int height = -1); diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp index 864547ae26..e05006c46e 100644 --- a/src/gpu/gl/GrGLTexture.cpp +++ b/src/gpu/gl/GrGLTexture.cpp @@ -15,14 +15,22 @@ // Because this class is virtually derived from GrSurface we must explicitly call its constructor. GrGLTexture::GrGLTexture(GrGLGpu* gpu, const GrSurfaceDesc& desc, const IDDesc& idDesc) : GrSurface(gpu, idDesc.fLifeCycle, desc) - , INHERITED(gpu, idDesc.fLifeCycle, desc) { + , INHERITED(gpu, idDesc.fLifeCycle, desc, false) { + this->init(desc, idDesc); + this->registerWithCache(); +} + +GrGLTexture::GrGLTexture(GrGLGpu* gpu, const GrSurfaceDesc& desc, const IDDesc& idDesc, + bool wasMipMapDataProvided) + : GrSurface(gpu, idDesc.fLifeCycle, desc) + , INHERITED(gpu, idDesc.fLifeCycle, desc, wasMipMapDataProvided) { this->init(desc, idDesc); this->registerWithCache(); } GrGLTexture::GrGLTexture(GrGLGpu* gpu, const GrSurfaceDesc& desc, const IDDesc& idDesc, Derived) : GrSurface(gpu, idDesc.fLifeCycle, desc) - , INHERITED(gpu, idDesc.fLifeCycle, desc) { + , INHERITED(gpu, idDesc.fLifeCycle, desc, false) { this->init(desc, idDesc); } diff --git a/src/gpu/gl/GrGLTexture.h b/src/gpu/gl/GrGLTexture.h index 8366a8dab0..937b8be13e 100644 --- a/src/gpu/gl/GrGLTexture.h +++ b/src/gpu/gl/GrGLTexture.h @@ -23,6 +23,7 @@ public: GrGLenum fMagFilter; GrGLenum fWrapS; GrGLenum fWrapT; + GrGLenum fMaxMipMapLevel; GrGLenum fSwizzleRGBA[4]; void invalidate() { memset(this, 0xff, sizeof(TexParams)); } }; @@ -33,6 +34,7 @@ public: }; GrGLTexture(GrGLGpu*, const GrSurfaceDesc&, const IDDesc&); + GrGLTexture(GrGLGpu*, const GrSurfaceDesc&, const IDDesc&, bool wasMipMapDataProvided); GrBackendObject getTextureHandle() const override; |