Chromium Code Reviews| Index: src/gpu/gl/GrGLGpu.cpp |
| diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp |
| index cb12fefc3291f48388275702a71e48d555ddd408..1b7817356a1ea055dbac2833e35e300fe3dc5326 100644 |
| --- a/src/gpu/gl/GrGLGpu.cpp |
| +++ b/src/gpu/gl/GrGLGpu.cpp |
| @@ -6,6 +6,9 @@ |
| */ |
| +#include <algorithm> |
| +#include <limits> |
| + |
| #include "GrGLGpu.h" |
| #include "GrGLGLSL.h" |
| #include "GrGLStencilAttachment.h" |
| @@ -426,7 +429,7 @@ GrTexture* GrGLGpu::onWrapBackendTexture(const GrBackendTextureDesc& desc, |
| GrSurfaceDesc surfDesc; |
| idDesc.fTextureID = static_cast<GrGLuint>(desc.fTextureHandle); |
| - |
| + |
| switch (ownership) { |
| case kAdopt_GrWrapOwnership: |
| idDesc.fLifeCycle = GrGpuResource::kAdopted_LifeCycle; |
| @@ -434,7 +437,7 @@ GrTexture* GrGLGpu::onWrapBackendTexture(const GrBackendTextureDesc& desc, |
| case kBorrow_GrWrapOwnership: |
| idDesc.fLifeCycle = GrGpuResource::kBorrowed_LifeCycle; |
| break; |
| - } |
| + } |
| // next line relies on GrBackendTextureDesc's flags matching GrTexture's |
| surfDesc.fFlags = (GrSurfaceFlags) desc.fFlags; |
| @@ -484,7 +487,7 @@ GrRenderTarget* GrGLGpu::onWrapBackendRenderTarget(const GrBackendRenderTargetDe |
| case kBorrow_GrWrapOwnership: |
| idDesc.fLifeCycle = GrGpuResource::kBorrowed_LifeCycle; |
| break; |
| - } |
| + } |
| idDesc.fSampleConfig = GrRenderTarget::kUnified_SampleConfig; |
| GrSurfaceDesc desc; |
| @@ -500,7 +503,7 @@ GrRenderTarget* GrGLGpu::onWrapBackendRenderTarget(const GrBackendRenderTargetDe |
| //////////////////////////////////////////////////////////////////////////////// |
| bool GrGLGpu::onGetWritePixelsInfo(GrSurface* dstSurface, int width, int height, |
| - size_t rowBytes, GrPixelConfig srcConfig, |
| + GrPixelConfig srcConfig, |
| DrawPreference* drawPreference, |
| WritePixelTempDrawInfo* tempDrawInfo) { |
| if (kIndex_8_GrPixelConfig == srcConfig || GrPixelConfigIsCompressed(dstSurface->config())) { |
| @@ -560,8 +563,8 @@ bool GrGLGpu::onGetWritePixelsInfo(GrSurface* dstSurface, int width, int height, |
| 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<SkMipMapLevel>& texels) { |
| GrGLTexture* glTex = static_cast<GrGLTexture*>(surface->asTexture()); |
| if (!glTex) { |
| return false; |
| @@ -579,19 +582,13 @@ 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(), buffer, false, left, top, width, |
| + success = this->uploadCompressedTexData(glTex->desc(), texels, false, left, top, width, |
| height); |
| } else { |
| - success = this->uploadTexData(glTex->desc(), false, left, top, width, height, config, |
| - buffer, rowBytes); |
| - } |
| - |
| - if (success) { |
| - glTex->texturePriv().dirtyMipMaps(true); |
| - return true; |
| + success = this->uploadTexData(glTex->desc(), false, left, top, width, height, config, texels); |
| } |
| - return false; |
| + return success; |
| } |
| static inline GrGLenum check_alloc_error(const GrSurfaceDesc& desc, |
| @@ -603,36 +600,17 @@ static inline GrGLenum check_alloc_error(const GrSurfaceDesc& desc, |
| } |
| } |
| -bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, |
| - bool isNewTexture, |
| - int left, int top, int width, int height, |
| - GrPixelConfig dataConfig, |
| - const void* data, |
| - size_t rowBytes) { |
| - SkASSERT(data || isNewTexture); |
| - |
| - // If we're uploading compressed data then we should be using uploadCompressedTexData |
| - SkASSERT(!GrPixelConfigIsCompressed(dataConfig)); |
| - |
| - size_t bpp = GrBytesPerPixel(dataConfig); |
| - if (!GrSurfacePriv::AdjustWritePixelParams(desc.fWidth, desc.fHeight, bpp, &left, &top, |
| - &width, &height, &data, &rowBytes)) { |
| - return false; |
| - } |
| - size_t trimRowBytes = width * bpp; |
| - |
| - // in case we need a temporary, trimmed copy of the src pixels |
| - SkAutoSMalloc<128 * 128> tempStorage; |
| - |
| - // We currently lazily create MIPMAPs when the we see a draw with |
| - // GrTextureParams::kMipMap_FilterMode. Using texture storage requires that the |
| - // MIP levels are all created when the texture is created. So for now we don't use |
| - // texture storage. |
| - bool useTexStorage = false && |
| - isNewTexture && |
| - this->glCaps().texStorageSupport(); |
| - |
| - if (useTexStorage && kGL_GrGLStandard == this->glStandard()) { |
| +/** |
| + * Determines if TexStorage can be used when creating a texture. |
| + * |
| + * @param caps The capabilities of the GL device. |
| + * @param standard The GL standard in use. |
| + * @param desc The surface descriptor for the texture being created. |
| + */ |
| +static bool can_use_tex_storage(const GrGLCaps& caps, const GrGLStandard& standard, |
| + const GrSurfaceDesc& desc) { |
| + bool useTexStorage = caps.texStorageSupport(); |
| + if (useTexStorage && kGL_GrGLStandard == standard) { |
| // 565 is not a sized internal format on desktop GL. So on desktop with |
| // 565 we always use an unsized internal format to let the system pick |
| // the best sized format to convert the 565 data to. Since TexStorage |
| @@ -640,59 +618,92 @@ bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, |
| useTexStorage = desc.fConfig != kRGB_565_GrPixelConfig; |
| } |
| - GrGLenum internalFormat = 0x0; // suppress warning |
| - GrGLenum externalFormat = 0x0; // suppress warning |
| - GrGLenum externalType = 0x0; // suppress warning |
| + return useTexStorage; |
| +} |
| +/** |
| + * Determines if sized internal formats are available for the texture being created. |
| + * |
| + * @param useTexStorage The result of a call to can_use_tex_storage(). |
| + * @param caps The capabilities of the GL device. |
| + * @param standard The GL standard in use. |
| + * @param version The GL version in use. |
| + * @param dataConfig The pixel configuration for the texture being created. |
| + */ |
| +static bool can_use_sized_format(bool useTexStorage, const GrGLCaps& caps, |
| + const GrGLStandard& standard, const GrGLVersion& version, |
| + GrPixelConfig dataConfig) { |
| // glTexStorage requires sized internal formats on both desktop and ES. ES2 requires an unsized |
| // format for glTexImage, unlike ES3 and desktop. |
| bool useSizedFormat = useTexStorage; |
| - if (kGL_GrGLStandard == this->glStandard() || |
| - (this->glVersion() >= GR_GL_VER(3, 0) && |
| + if (kGL_GrGLStandard == standard || |
| + (version >= GR_GL_VER(3, 0) && |
| // ES3 only works with sized BGRA8 format if "GL_APPLE_texture_format_BGRA8888" enabled |
| - (kBGRA_8888_GrPixelConfig != dataConfig || !this->glCaps().bgraIsInternalFormat()))) { |
| + (kBGRA_8888_GrPixelConfig != dataConfig || !caps.bgraIsInternalFormat()))) { |
| useSizedFormat = true; |
| } |
| - if (!this->configToGLFormats(dataConfig, useSizedFormat, &internalFormat, |
| - &externalFormat, &externalType)) { |
| - return false; |
| - } |
| + return useSizedFormat; |
| +} |
| - /* |
| - * 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. |
| - */ |
| - bool restoreGLRowLength = false; |
| - bool swFlipY = false; |
| - bool glFlipY = false; |
| - if (data) { |
| - if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) { |
| - if (this->glCaps().unpackFlipYSupport()) { |
| - glFlipY = true; |
| - } else { |
| - swFlipY = true; |
| - } |
| +/** |
| + * Prior to a texture being created, the image may need to be flipped vertically. This function |
| + * prepares the texels for texture creation. |
| + * |
| + * @param desc The surface descriptor for the texture being created. |
| + * @param caps The capabilities of the GL device. |
| + * @param interface The GL interface in use. |
| + * @param swFlipY Should software be used when flipping a texture vertically? |
| + * @param glFlipY Should GL be used when flipping a texture vertically? |
| + * @param width The width of the texture in texels. |
| + * @param height The height of the texture in texels. |
| + * @param bpp The bits per pixel (or texel, really) of the texture. |
| + * @param texels An array of mipmap levels which contain the texel data at that level. |
| + * @param tempStorage In the case where the image needs to be flipped vertically, it will |
| + * use tempStorage as a buffer. |
| + * @param restoreGLRowLength After the texture is created, will the GL row length unpacking need |
| + * to be restored? |
| + */ |
| +static void prepare_image_for_writing_to_texture(const GrSurfaceDesc& desc, const GrGLCaps& caps, |
| + const GrGLInterface& interface, bool swFlipY, |
| + bool glFlipY, int bpp, |
| + SkTArray<SkMipMapLevel>& texels, |
| + SkAutoSMalloc<128 * 128>& tempStorage, |
| + bool* restoreGLRowLength) { |
| + for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) { |
| + if (texels[currentMipLevel].fTexels == nullptr) { |
| + continue; |
| } |
| - if (this->glCaps().unpackRowLengthSupport() && !swFlipY) { |
| + |
| + const size_t trimRowBytes = texels[currentMipLevel].fWidth * 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 = texels[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)); |
| - restoreGLRowLength = true; |
| + GR_GL_CALL(&interface, PixelStorei(GR_GL_UNPACK_ROW_LENGTH, rowLength)); |
| + *restoreGLRowLength = true; |
| } |
| } else { |
| if (trimRowBytes != rowBytes || swFlipY) { |
| + const uint32_t height = texels[currentMipLevel].fHeight; |
| // copy data into our new storage, skipping the trailing bytes |
| - size_t trimSize = height * trimRowBytes; |
| - const char* src = (const char*)data; |
| - if (swFlipY) { |
| + const size_t trimSize = height * trimRowBytes; |
| + const char* src = (const char*)texels[currentMipLevel].fTexels; |
| + if (swFlipY && height >= 1) { |
| src += (height - 1) * rowBytes; |
| } |
| char* dst = (char*)tempStorage.reset(trimSize); |
| - for (int y = 0; y < height; y++) { |
| + for (uint32_t y = 0; y < height; y++) { |
| memcpy(dst, src, trimRowBytes); |
| if (swFlipY) { |
| src -= rowBytes; |
| @@ -702,70 +713,291 @@ bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, |
| dst += trimRowBytes; |
| } |
| // now point data to our copied version |
| - data = tempStorage.get(); |
| + texels[currentMipLevel] = SkMipMapLevel(tempStorage.get(), trimRowBytes, |
| + texels[currentMipLevel].fWidth, |
| + texels[currentMipLevel].fHeight); |
| } |
| } |
| 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, |
| - static_cast<GrGLint>(GrUnpackAlignment(dataConfig)))); |
| + GR_GL_CALL(&interface, PixelStorei(GR_GL_UNPACK_ALIGNMENT, |
| + static_cast<GrGLint>(GrUnpackAlignment(desc.fConfig)))); |
| } |
| - bool succeeded = true; |
| - if (isNewTexture && |
| - 0 == left && 0 == top && |
| - desc.fWidth == width && desc.fHeight == height) { |
| - CLEAR_ERROR_BEFORE_ALLOC(this->glInterface()); |
| - if (useTexStorage) { |
| - // We never resize or change formats of textures. |
| - GL_ALLOC_CALL(this->glInterface(), |
| - TexStorage2D(GR_GL_TEXTURE_2D, |
| - 1, // levels |
| - internalFormat, |
| - desc.fWidth, desc.fHeight)); |
| +} |
| + |
| +/** |
| + * 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 useTexStorage The result of a call to can_use_tex_storage(). |
| + * @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 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, |
| + bool useTexStorage, |
| + GrGLenum internalFormat, |
| + GrGLenum externalFormat, |
| + GrGLenum externalType, |
| + const SkTArray<SkMipMapLevel>& texels, |
| + bool* succeeded) { |
| + CLEAR_ERROR_BEFORE_ALLOC(&interface); |
| + if (useTexStorage) { |
| + // We never resize or change formats of textures. |
| + GL_ALLOC_CALL(&interface, |
| + TexStorage2D(GR_GL_TEXTURE_2D, |
| + texels.count(), |
| + internalFormat, |
| + desc.fWidth, desc.fHeight)); |
| + |
| + GrGLenum error = check_alloc_error(desc, &interface); |
| + if (error != GR_GL_NO_ERROR) { |
| + *succeeded = false; |
| } else { |
| - GL_ALLOC_CALL(this->glInterface(), |
| + for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) { |
| + const void* currentMipData = texels[currentMipLevel].fTexels; |
| + if (currentMipData == nullptr) { |
| + continue; |
| + } |
| + |
| + GR_GL_CALL(&interface, |
| + TexSubImage2D(GR_GL_TEXTURE_2D, |
| + currentMipLevel, |
| + 0, // left |
| + 0, // top |
| + texels[currentMipLevel].fWidth, |
| + texels[currentMipLevel].fHeight, |
| + externalFormat, externalType, |
| + currentMipData)); |
| + } |
| + *succeeded = true; |
| + } |
| + } else { |
| + *succeeded = true; |
| + for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) { |
| + GL_ALLOC_CALL(&interface, |
| TexImage2D(GR_GL_TEXTURE_2D, |
| - 0, // level |
| + currentMipLevel, |
| internalFormat, |
| - desc.fWidth, desc.fHeight, |
| + texels[currentMipLevel].fWidth, |
| + texels[currentMipLevel].fHeight, |
| 0, // border |
| externalFormat, externalType, |
| - data)); |
| + texels[currentMipLevel].fTexels)); |
| + GrGLenum error = check_alloc_error(desc, &interface); |
| + if (error != GR_GL_NO_ERROR) { |
| + *succeeded = false; |
| + break; |
| + } |
| } |
| - GrGLenum error = check_alloc_error(desc, this->glInterface()); |
| + } |
| +} |
| + |
| +/** |
| + * 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 useTexStorage The result of a call to can_use_tex_storage(). |
| + * @param internalFormat The data format used for the internal storage of the texture. |
| + * @param texels The texel data of the texture being created. |
| + */ |
| +static bool allocate_and_populate_compressed_texture(const GrSurfaceDesc& desc, |
| + const GrGLInterface& interface, |
| + bool useTexStorage, GrGLenum internalFormat, |
| + const SkTArray<SkMipMapLevel>& texels) { |
| + CLEAR_ERROR_BEFORE_ALLOC(&interface); |
| + if (useTexStorage) { |
| + // We never resize or change formats of textures. |
| + GL_ALLOC_CALL(&interface, |
| + TexStorage2D(GR_GL_TEXTURE_2D, |
| + texels.count(), |
| + internalFormat, |
| + desc.fWidth, desc.fHeight)); |
| + GrGLenum error = check_alloc_error(desc, &interface); |
| if (error != GR_GL_NO_ERROR) { |
| - succeeded = false; |
| + return false; |
| } else { |
| - // if we have data and we used TexStorage to create the texture, we |
| - // now upload with TexSubImage. |
| - if (data && useTexStorage) { |
| - GL_CALL(TexSubImage2D(GR_GL_TEXTURE_2D, |
| - 0, // level |
| - left, top, |
| - width, height, |
| - externalFormat, externalType, |
| - data)); |
| + for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) { |
| + const void* currentMipData = texels[currentMipLevel].fTexels; |
| + if (currentMipData == nullptr) { |
| + continue; |
| + } |
| + |
| + const uint32_t width = texels[currentMipLevel].fWidth; |
| + const uint32_t height = texels[currentMipLevel].fHeight; |
| + |
| + // 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); |
| + GR_GL_CALL(&interface, CompressedTexSubImage2D(GR_GL_TEXTURE_2D, |
| + currentMipLevel, |
| + 0, // left |
| + 0, // top |
| + width, height, |
| + internalFormat, SkToInt(dataSize), |
| + currentMipData)); |
| } |
| } |
| } else { |
| - if (swFlipY || glFlipY) { |
| - top = desc.fHeight - (top + height); |
| + for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) { |
| + const uint32_t width = texels[currentMipLevel].fWidth; |
| + const uint32_t height = texels[currentMipLevel].fHeight; |
| + |
| + // 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); |
| + |
| + GL_ALLOC_CALL(&interface, |
| + CompressedTexImage2D(GR_GL_TEXTURE_2D, |
| + texels.count(), |
| + internalFormat, |
| + width, height, |
| + 0, // border |
| + SkToInt(dataSize), |
| + texels[currentMipLevel].fTexels)); |
| + |
| + GrGLenum error = check_alloc_error(desc, &interface); |
| + if (error != GR_GL_NO_ERROR) { |
| + return false; |
| + } |
| } |
| - GL_CALL(TexSubImage2D(GR_GL_TEXTURE_2D, |
| - 0, // level |
| - left, top, |
| - width, height, |
| - externalFormat, externalType, data)); |
| } |
| + 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(this->glCaps().unpackRowLengthSupport()); |
| - GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0)); |
| + SkASSERT(caps.unpackRowLengthSupport()); |
| + GR_GL_CALL(&interface, PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0)); |
| } |
| if (glFlipY) { |
| - GL_CALL(PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_FALSE)); |
| + GR_GL_CALL(&interface, PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_FALSE)); |
| } |
| +} |
| + |
| +bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, |
| + bool isNewTexture, |
| + int left, int top, int width, int height, |
| + GrPixelConfig dataConfig, |
| + const SkTArray<SkMipMapLevel>& texels) { |
| + // If we're uploading compressed data then we should be using uploadCompressedTexData |
| + SkASSERT(!GrPixelConfigIsCompressed(dataConfig)); |
| + |
| + SkTArray<SkMipMapLevel> texelsCopy(texels); |
| + |
| + for (int currentMipLevel = texelsCopy.count() - 1; currentMipLevel >= 0; currentMipLevel--) { |
| + //SkASSERT(texelsCopy[currentMipLevel].fTexels || isNewTexture); |
| + } |
| + |
| + |
| + const GrGLInterface* interface = this->glInterface(); |
| + if (interface == nullptr) { |
| + return false; |
| + } |
| + const GrGLCaps& caps = this->glCaps(); |
| + GrGLStandard standard = this->glStandard(); |
| + GrGLVersion version = this->glVersion(); |
| + |
| + size_t bpp = GrBytesPerPixel(dataConfig); |
| + for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) { |
| + if (texelsCopy[currentMipLevel].fTexels == nullptr) { |
| + continue; |
| + } |
| + |
| + if (texelsCopy[currentMipLevel].fHeight > std::numeric_limits<int>::max() |
|
bsalomon
2015/09/30 18:01:29
SK_MaxI32
cblume
2015/10/08 09:27:57
Done.
|
| + || texelsCopy[currentMipLevel].fWidth > std::numeric_limits<int>::max()) { |
|
bsalomon
2015/09/30 18:01:29
SK_MaxI32
cblume
2015/10/08 09:27:57
Done.
|
| + return false; |
| + } |
| + int currentMipHeight = texelsCopy[currentMipLevel].fHeight; |
| + int currentMipWidth = texelsCopy[currentMipLevel].fWidth; |
| + if (!GrSurfacePriv::AdjustWritePixelParams(desc.fWidth, desc.fHeight, bpp, &left, &top, |
| + ¤tMipWidth, |
| + ¤tMipHeight, |
| + &texelsCopy[currentMipLevel].fTexels, |
| + &texelsCopy[currentMipLevel].fRowBytes)) { |
| + return false; |
| + } |
| + if (currentMipWidth < 0 || currentMipHeight < 0) { |
| + return false; |
| + } |
| + texelsCopy[currentMipLevel].fWidth = currentMipWidth; |
| + texelsCopy[currentMipLevel].fHeight = currentMipHeight; |
| + } |
| + |
| + bool useTexStorage = can_use_tex_storage(caps, standard, desc); |
| + bool useSizedFormat = can_use_sized_format(useTexStorage, caps, standard, version, dataConfig); |
| + |
| + GrGLenum internalFormat = 0x0; // suppress warning |
| + GrGLenum externalFormat = 0x0; // suppress warning |
| + GrGLenum externalType = 0x0; // suppress warning |
| + |
| + if (!this->configToGLFormats(dataConfig, useSizedFormat, &internalFormat, |
| + &externalFormat, &externalType)) { |
| + return false; |
| + } |
| + |
| + bool swFlipY = false; |
| + bool glFlipY = false; |
| + |
| + if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) { |
| + if (caps.unpackFlipYSupport()) { |
| + glFlipY = true; |
| + } else { |
| + swFlipY = true; |
| + } |
| + } |
| + |
| + bool restoreGLRowLength = false; |
| + |
| + // in case we need a temporary, trimmed copy of the src pixels |
| + SkAutoSMalloc<128 * 128> tempStorage; |
| + prepare_image_for_writing_to_texture(desc, caps, *interface, swFlipY, glFlipY, bpp, texelsCopy, |
| + tempStorage, &restoreGLRowLength); |
| + bool succeeded = true; |
| + if (isNewTexture && |
| + 0 == left && 0 == top && |
| + desc.fWidth == width && desc.fHeight == height) { |
| + allocate_and_populate_uncompressed_texture(desc, *interface, useTexStorage, internalFormat, |
| + externalFormat, externalType, texelsCopy, |
| + &succeeded); |
| + } else { |
| + if (swFlipY || glFlipY) { |
| + top = desc.fHeight - (top + height); |
| + } |
| + for (int currentMipLevel = 0; currentMipLevel < texelsCopy.count(); currentMipLevel++) { |
| + if (texelsCopy[currentMipLevel].fTexels == nullptr) { |
| + continue; |
| + } |
| + |
| + GL_CALL(TexSubImage2D(GR_GL_TEXTURE_2D, |
| + currentMipLevel, |
| + left, top, |
| + texelsCopy[currentMipLevel].fWidth, |
| + texelsCopy[currentMipLevel].fHeight, |
| + externalFormat, externalType, |
| + texelsCopy[currentMipLevel].fTexels)); |
| + } |
| + } |
| + |
| + restore_pixelstore_state(*interface, caps, restoreGLRowLength, glFlipY); |
| + |
| return succeeded; |
| } |
| @@ -775,14 +1007,21 @@ bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, |
| // the proper upload semantics. Then users can construct this function how they |
| // see fit if they want to go against the "standard" way to do it. |
| bool GrGLGpu::uploadCompressedTexData(const GrSurfaceDesc& desc, |
| - const void* data, |
| + const SkTArray<SkMipMapLevel>& texels, |
| bool isNewTexture, |
| int left, int top, int width, int height) { |
| - SkASSERT(data || isNewTexture); |
| + SkASSERT(isNewTexture); |
| // No support for software flip y, yet... |
| SkASSERT(kBottomLeft_GrSurfaceOrigin != desc.fOrigin); |
| + const GrGLInterface* interface = this->glInterface(); |
| + if (interface == nullptr) { |
|
bsalomon
2015/09/30 18:01:29
This can't happen, an interface is required to cre
cblume
2015/10/08 09:27:57
Done.
|
| + return false; |
| + } |
| + const GrGLCaps& caps = this->glCaps(); |
| + GrGLStandard standard = this->glStandard(); |
| + |
| if (-1 == width) { |
| width = desc.fWidth; |
| } |
| @@ -801,9 +1040,7 @@ 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); |
| + bool useTexStorage = can_use_tex_storage(caps, standard, desc); |
| // We only need the internal format for compressed 2D textures. |
| GrGLenum internalFormat = 0; |
| @@ -812,31 +1049,30 @@ bool GrGLGpu::uploadCompressedTexData(const GrSurfaceDesc& desc, |
| } |
| if (isNewTexture) { |
| - CLEAR_ERROR_BEFORE_ALLOC(this->glInterface()); |
| - GL_ALLOC_CALL(this->glInterface(), |
| - CompressedTexImage2D(GR_GL_TEXTURE_2D, |
| - 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, useTexStorage, |
| + internalFormat, texels); |
| } else { |
| // Paletted textures can't be updated. |
| if (GR_GL_PALETTE8_RGBA8 == internalFormat) { |
| return false; |
| } |
| - GL_CALL(CompressedTexSubImage2D(GR_GL_TEXTURE_2D, |
| - 0, // level |
| - left, top, |
| - width, height, |
| - internalFormat, |
| - SkToInt(dataSize), |
| - data)); |
| + for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) { |
| + if (texels[currentMipLevel].fTexels == nullptr) { |
| + continue; |
| + } |
| + |
| + // 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); |
| + GL_CALL(CompressedTexSubImage2D(GR_GL_TEXTURE_2D, |
| + currentMipLevel, |
| + left, top, |
| + texels[currentMipLevel].fWidth, |
| + texels[currentMipLevel].fHeight, |
| + internalFormat, |
| + dataSize, |
| + texels[currentMipLevel].fTexels)); |
| + } |
| } |
| return true; |
| @@ -942,9 +1178,9 @@ bool GrGLGpu::createRenderTargetObjects(const GrSurfaceDesc& desc, |
| fStats.incRenderTargetBinds(); |
| GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, idDesc->fRTFBOID)); |
| GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, |
| - GR_GL_COLOR_ATTACHMENT0, |
| - GR_GL_RENDERBUFFER, |
| - idDesc->fMSColorRenderbufferID)); |
| + GR_GL_COLOR_ATTACHMENT0, |
| + GR_GL_RENDERBUFFER, |
| + idDesc->fMSColorRenderbufferID)); |
| if ((desc.fFlags & kCheckAllocation_GrSurfaceFlag) || |
| !this->glCaps().isConfigVerifiedColorAttachment(desc.fConfig)) { |
| GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER)); |
| @@ -1004,9 +1240,43 @@ static size_t as_size_t(int x) { |
| } |
| #endif |
| +static GrGLTexture::IDDesc generate_and_bind_gl_texture(const GrGLInterface* interface, |
| + GrGpuResource::LifeCycle lifeCycle) { |
| + GrGLTexture::IDDesc idDesc; |
| + GR_GL_CALL(interface, GenTextures(1, &idDesc.fTextureID)); |
| + idDesc.fLifeCycle = lifeCycle; |
| + return idDesc; |
| +} |
| + |
| +static GrGLTexture::TexParams set_initial_texture_params(const GrGLInterface* interface) { |
| + // 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; |
| + GR_GL_CALL(interface, TexParameteri(GR_GL_TEXTURE_2D, |
| + GR_GL_TEXTURE_MAG_FILTER, |
| + initialTexParams.fMagFilter)); |
| + GR_GL_CALL(interface, TexParameteri(GR_GL_TEXTURE_2D, |
| + GR_GL_TEXTURE_MIN_FILTER, |
| + initialTexParams.fMinFilter)); |
| + GR_GL_CALL(interface, TexParameteri(GR_GL_TEXTURE_2D, |
| + GR_GL_TEXTURE_WRAP_S, |
| + initialTexParams.fWrapS)); |
| + GR_GL_CALL(interface, TexParameteri(GR_GL_TEXTURE_2D, |
| + GR_GL_TEXTURE_WRAP_T, |
| + initialTexParams.fWrapT)); |
| + return initialTexParams; |
| +} |
| + |
| GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc, |
| GrGpuResource::LifeCycle lifeCycle, |
| - const void* srcData, size_t rowBytes) { |
| + const SkTArray<SkMipMapLevel>& 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."); |
| @@ -1015,10 +1285,7 @@ GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc, |
| bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag); |
| - GrGLTexture::IDDesc idDesc; |
| - GL_CALL(GenTextures(1, &idDesc.fTextureID)); |
| - idDesc.fLifeCycle = lifeCycle; |
| - |
| + GrGLTexture::IDDesc idDesc = generate_and_bind_gl_texture(this->glInterface(), lifeCycle); |
| if (!idDesc.fTextureID) { |
| return return_null_texture(); |
| } |
| @@ -1033,31 +1300,11 @@ GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc, |
| GR_GL_FRAMEBUFFER_ATTACHMENT)); |
| } |
| - // 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(GR_GL_TEXTURE_2D, |
| - GR_GL_TEXTURE_MAG_FILTER, |
| - initialTexParams.fMagFilter)); |
| - GL_CALL(TexParameteri(GR_GL_TEXTURE_2D, |
| - GR_GL_TEXTURE_MIN_FILTER, |
| - initialTexParams.fMinFilter)); |
| - GL_CALL(TexParameteri(GR_GL_TEXTURE_2D, |
| - GR_GL_TEXTURE_WRAP_S, |
| - initialTexParams.fWrapS)); |
| - GL_CALL(TexParameteri(GR_GL_TEXTURE_2D, |
| - GR_GL_TEXTURE_WRAP_T, |
| - initialTexParams.fWrapT)); |
| + GrGLTexture::TexParams initialTexParams = set_initial_texture_params(this->glInterface()); |
| + |
| if (!this->uploadTexData(desc, true, 0, 0, |
| desc.fWidth, desc.fHeight, |
| - desc.fConfig, srcData, rowBytes)) { |
| + desc.fConfig, texels)) { |
| GL_CALL(DeleteTextures(1, &idDesc.fTextureID)); |
| return return_null_texture(); |
| } |
| @@ -1086,16 +1333,13 @@ GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc, |
| GrTexture* GrGLGpu::onCreateCompressedTexture(const GrSurfaceDesc& desc, |
| GrGpuResource::LifeCycle lifeCycle, |
| - const void* srcData) { |
| + const SkTArray<SkMipMapLevel>& texels) { |
| // Make sure that we're not flipping Y. |
| if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) { |
| return return_null_texture(); |
| } |
| - GrGLTexture::IDDesc idDesc; |
| - GL_CALL(GenTextures(1, &idDesc.fTextureID)); |
| - idDesc.fLifeCycle = lifeCycle; |
| - |
| + auto idDesc = generate_and_bind_gl_texture(this->glInterface(), lifeCycle); |
|
bsalomon
2015/09/30 18:01:29
-auto
cblume
2015/10/08 09:27:57
Done.
|
| if (!idDesc.fTextureID) { |
| return return_null_texture(); |
| } |
| @@ -1103,30 +1347,9 @@ GrTexture* GrGLGpu::onCreateCompressedTexture(const GrSurfaceDesc& desc, |
| this->setScratchTextureUnit(); |
| GL_CALL(BindTexture(GR_GL_TEXTURE_2D, idDesc.fTextureID)); |
| - // 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(GR_GL_TEXTURE_2D, |
| - GR_GL_TEXTURE_MAG_FILTER, |
| - initialTexParams.fMagFilter)); |
| - GL_CALL(TexParameteri(GR_GL_TEXTURE_2D, |
| - GR_GL_TEXTURE_MIN_FILTER, |
| - initialTexParams.fMinFilter)); |
| - GL_CALL(TexParameteri(GR_GL_TEXTURE_2D, |
| - GR_GL_TEXTURE_WRAP_S, |
| - initialTexParams.fWrapS)); |
| - GL_CALL(TexParameteri(GR_GL_TEXTURE_2D, |
| - GR_GL_TEXTURE_WRAP_T, |
| - initialTexParams.fWrapT)); |
| - |
| - if (!this->uploadCompressedTexData(desc, srcData)) { |
| + auto initialTexParams = set_initial_texture_params(this->glInterface()); |
|
bsalomon
2015/09/30 18:01:29
-auto
cblume
2015/10/08 09:27:57
Done.
|
| + |
| + if (!this->uploadCompressedTexData(desc, texels)) { |
| GL_CALL(DeleteTextures(1, &idDesc.fTextureID)); |
| return return_null_texture(); |
| } |
| @@ -2550,7 +2773,7 @@ bool GrGLGpu::configToGLFormats(GrPixelConfig config, |
| *externalType = GR_GL_HALF_FLOAT_OES; |
| } |
| break; |
| - |
| + |
| case kRGBA_half_GrPixelConfig: |
| *internalFormat = GR_GL_RGBA16F; |
| *externalFormat = GR_GL_RGBA; |
| @@ -2745,7 +2968,7 @@ bool GrGLGpu::onCopySurface(GrSurface* dst, |
| this->copySurfaceAsDraw(dst, src, srcRect, dstPoint); |
| return true; |
| } |
| - |
| + |
| if (can_copy_texsubimage(dst, src, this)) { |
| this->copySurfaceAsCopyTexSubImage(dst, src, srcRect, dstPoint); |
| return true; |
| @@ -2769,7 +2992,7 @@ void GrGLGpu::createCopyProgram() { |
| GrGLShaderVar uTexture("u_texture", kSampler2D_GrSLType, GrShaderVar::kUniform_TypeModifier); |
| GrGLShaderVar vTexCoord("v_texCoord", kVec2f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier); |
| GrGLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier); |
| - |
| + |
| SkString vshaderTxt(version); |
| aVertex.appendDecl(this->ctxInfo(), &vshaderTxt); |
| vshaderTxt.append(";"); |
| @@ -2779,7 +3002,7 @@ void GrGLGpu::createCopyProgram() { |
| vshaderTxt.append(";"); |
| vTexCoord.appendDecl(this->ctxInfo(), &vshaderTxt); |
| vshaderTxt.append(";"); |
| - |
| + |
| vshaderTxt.append( |
| "// Copy Program VS\n" |
| "void main() {" |
| @@ -2813,7 +3036,7 @@ void GrGLGpu::createCopyProgram() { |
| fsOutName, |
| GrGLSLTexture2DFunctionName(kVec2f_GrSLType, this->glslGeneration()) |
| ); |
| - |
| + |
| GL_CALL_RET(fCopyProgram.fProgram, CreateProgram()); |
| const char* str; |
| GrGLint length; |