| Index: src/gpu/gl/GrGLGpu.cpp
|
| diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
|
| index 8e6fbadc7b98c3b5e4879f1ca4ffad73be6f585b..071b11383d1691cf134207da8efeaaaefec8fb41 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));
|
|
|