Separate texture creation from uploading in GrGpu subclasses.
GrGpu base class still allows creation with initial data, but separated
at subclass level into create and then write pixels.
GrGpu handles determining which levels need clearing and GrGpu
subclasses take a mask and clear levels with mask bit set.
GrGLGpu uses three pronged clear strategy:
glClearTexImage() if supported, glClear() if format is FBO bindable, and
lastly glTexSubImage2D with zero'ed buffer.
Change-Id: I65fb1e60eed8f9d0896d686d3baeb10b57ff8f39
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/236676
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 8cbf08b..0917e79 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -719,22 +719,20 @@
GrColorType colorType,
GrWrapOwnership ownership,
GrWrapCacheable cacheable) {
+ const GrGLCaps& caps = this->glCaps();
+
GrGLTexture::Desc desc;
if (!check_backend_texture(backendTex, colorType, this->glCaps(), &desc)) {
return nullptr;
}
+ SkASSERT(caps.isFormatRenderable(desc.fFormat, sampleCnt));
+ SkASSERT(caps.isFormatTexturable(desc.fFormat));
// We don't support rendering to a EXTERNAL texture.
if (GR_GL_TEXTURE_EXTERNAL == desc.fTarget) {
return nullptr;
}
- const GrGLCaps& caps = this->glCaps();
-
- if (!caps.isFormatRenderable(desc.fFormat, sampleCnt)) {
- return nullptr;
- }
-
if (kBorrow_GrWrapOwnership == ownership) {
desc.fOwnership = GrBackendObjectOwnership::kBorrowed;
} else {
@@ -846,8 +844,8 @@
SkASSERT(!GrGLFormatIsCompressed(glTex->format()));
return this->uploadTexData(glTex->format(), surfaceColorType, glTex->width(), glTex->height(),
- glTex->target(), kWrite_UploadType, left, top,width,
- height, srcColorType, texels, mipLevelCount);
+ glTex->target(), left, top, width, height, srcColorType, texels,
+ mipLevelCount);
}
bool GrGLGpu::onTransferPixelsTo(GrTexture* texture, int left, int top, int width, int height,
@@ -896,13 +894,11 @@
}
GrGLFormat textureFormat = glTex->format();
- // Internal format comes from the texture desc.
- GrGLenum internalFormat;
// External format and type come from the upload data.
GrGLenum externalFormat = 0;
GrGLenum externalType = 0;
- this->glCaps().getTexImageFormats(textureFormat, textureColorType, bufferColorType,
- &internalFormat, &externalFormat, &externalType);
+ this->glCaps().getTexSubImageExternalFormatAndType(
+ textureFormat, textureColorType, bufferColorType, &externalFormat, &externalType);
if (!externalFormat || !externalType) {
return false;
}
@@ -933,178 +929,6 @@
dstColorType, offsetAsPtr, width);
}
-/**
- * Creates storage space for the texture and fills it with texels.
- *
- * @param format The format of the texture.
- * @param interface The GL interface in use.
- * @param caps The capabilities of the GL device.
- * @param target Which bound texture to target (GR_GL_TEXTURE_2D, e.g.)
- * @param internalFormat The data format used for the internal storage of the texture. May be sized.
- * @param internalFormatForTexStorage The data format used for the TexStorage API. Must be sized.
- * @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 dataBpp The bytes per pixel of the data in texels.
- * @param texels The texel data of the texture being created.
- * @param mipLevelCount Number of mipmap levels
- * @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_texture(GrGLFormat format,
- const GrGLInterface& interface,
- const GrGLCaps& caps,
- GrGLenum target,
- GrGLenum internalFormat,
- GrGLenum internalFormatForTexStorage,
- GrGLenum externalFormat,
- GrGLenum externalType,
- size_t dataBpp,
- const GrMipLevel texels[],
- int mipLevelCount,
- int baseWidth,
- int baseHeight,
- bool* changedUnpackRowLength,
- GrMipMapsStatus* mipMapsStatus) {
- CLEAR_ERROR_BEFORE_ALLOC(&interface);
-
- if (caps.formatSupportsTexStorage(format)) {
- // We never resize or change formats of textures.
- GL_ALLOC_CALL(&interface,
- TexStorage2D(target, SkTMax(mipLevelCount, 1), internalFormatForTexStorage,
- baseWidth, baseHeight));
- GrGLenum error = CHECK_ALLOC_ERROR(&interface);
- if (error != GR_GL_NO_ERROR) {
- return false;
- } else {
- for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
- const void* currentMipData = texels[currentMipLevel].fPixels;
- if (currentMipData == nullptr) {
- if (mipMapsStatus) {
- *mipMapsStatus = GrMipMapsStatus::kDirty;
- }
- continue;
- }
- int twoToTheMipLevel = 1 << currentMipLevel;
- const int currentWidth = SkTMax(1, baseWidth / twoToTheMipLevel);
- const int currentHeight = SkTMax(1, baseHeight / twoToTheMipLevel);
-
- if (texels[currentMipLevel].fPixels) {
- const size_t trimRowBytes = currentWidth * dataBpp;
- const size_t rowBytes = texels[currentMipLevel].fRowBytes;
- if (rowBytes != trimRowBytes) {
- SkASSERT(caps.writePixelsRowBytesSupport());
- GrGLint rowLength = static_cast<GrGLint>(rowBytes / dataBpp);
- GR_GL_CALL(&interface, PixelStorei(GR_GL_UNPACK_ROW_LENGTH, rowLength));
- *changedUnpackRowLength = true;
- } else if (*changedUnpackRowLength) {
- SkASSERT(caps.writePixelsRowBytesSupport());
- GR_GL_CALL(&interface, PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
- *changedUnpackRowLength = false;
- }
- }
-
- GR_GL_CALL(&interface,
- TexSubImage2D(target,
- currentMipLevel,
- 0, // left
- 0, // top
- currentWidth,
- currentHeight,
- externalFormat, externalType,
- currentMipData));
- }
- return true;
- }
- } else {
- if (!mipLevelCount) {
- GL_ALLOC_CALL(&interface,
- TexImage2D(target,
- 0,
- internalFormat,
- baseWidth,
- baseHeight,
- 0, // border
- externalFormat, externalType,
- nullptr));
- GrGLenum error = CHECK_ALLOC_ERROR(&interface);
- if (error != GR_GL_NO_ERROR) {
- return false;
- }
- } else {
- for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
- int twoToTheMipLevel = 1 << currentMipLevel;
- const int currentWidth = SkTMax(1, baseWidth / twoToTheMipLevel);
- const int currentHeight = SkTMax(1, baseHeight / twoToTheMipLevel);
-
- const void* currentMipData = texels[currentMipLevel].fPixels;
- if (currentMipData) {
- const size_t trimRowBytes = currentWidth * dataBpp;
- const size_t rowBytes = texels[currentMipLevel].fRowBytes;
- if (rowBytes != trimRowBytes) {
- SkASSERT(caps.writePixelsRowBytesSupport());
- GrGLint rowLength = static_cast<GrGLint>(rowBytes / dataBpp);
- GR_GL_CALL(&interface, PixelStorei(GR_GL_UNPACK_ROW_LENGTH, rowLength));
- *changedUnpackRowLength = true;
- } else if (*changedUnpackRowLength) {
- SkASSERT(caps.writePixelsRowBytesSupport());
- GR_GL_CALL(&interface, PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
- *changedUnpackRowLength = false;
- }
- } else if (mipMapsStatus) {
- *mipMapsStatus = GrMipMapsStatus::kDirty;
- }
-
- // We are considering modifying the interface to GrGpu to no longer allow data to
- // be provided when creating a texture. To test whether that is feasible for
- // performance on ES2 GPUs without tex storage we're calling glTexImage2D and then
- // glTexSubImage2D and hoping we don't get any performance regressions.
- GL_ALLOC_CALL(&interface,
- TexImage2D(target,
- currentMipLevel,
- internalFormat,
- currentWidth,
- currentHeight,
- 0, // border
- externalFormat, externalType,
- nullptr));
- if (currentMipData) {
- GR_GL_CALL(&interface,
- TexSubImage2D(target,
- currentMipLevel,
- 0, 0,
- currentWidth,
- currentHeight,
- externalFormat,
- externalType,
- currentMipData));
- }
- GrGLenum error = CHECK_ALLOC_ERROR(&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) {
- if (restoreGLRowLength) {
- SkASSERT(caps.writePixelsRowBytesSupport());
- GR_GL_CALL(&interface, PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
- }
-}
-
void GrGLGpu::unbindCpuToGpuXferBuffer() {
auto* xferBufferState = this->hwBufferState(GrGpuBufferType::kXferCpuToGpu);
if (!xferBufferState->fBoundBufferUniqueID.isInvalid()) {
@@ -1113,11 +937,10 @@
}
}
-bool GrGLGpu::uploadTexData(GrGLFormat textureFormat, GrColorType textureColorType,
- int texWidth, int texHeight, GrGLenum target, UploadType uploadType,
- int left, int top, int width, int height, GrColorType srcColorType,
- const GrMipLevel texels[],int mipLevelCount,
- GrMipMapsStatus* mipMapsStatus) {
+bool GrGLGpu::uploadTexData(GrGLFormat textureFormat, GrColorType textureColorType, int texWidth,
+ int texHeight, GrGLenum target, int left, int top, int width,
+ int height, GrColorType srcColorType, const GrMipLevel texels[],
+ int mipLevelCount, GrMipMapsStatus* mipMapsStatus) {
// If we're uploading compressed data then we should be using uploadCompressedTexData
SkASSERT(!GrGLFormatIsCompressed(textureFormat));
@@ -1141,19 +964,15 @@
return false;
}
- // Internal format comes from the texture desc.
- GrGLenum internalFormat;
// External format and type come from the upload data.
GrGLenum externalFormat;
GrGLenum externalType;
- this->glCaps().getTexImageFormats(textureFormat, textureColorType, srcColorType,
- &internalFormat, &externalFormat, &externalType);
+ this->glCaps().getTexSubImageExternalFormatAndType(
+ textureFormat, textureColorType, srcColorType, &externalFormat, &externalType);
if (!externalFormat || !externalType) {
return false;
}
- GrGLenum internalFormatForTexStorage = this->glCaps().getSizedInternalFormat(textureFormat);
-
/*
* Check whether to allocate a temporary buffer for flipping y or
* because our srcData has extra bytes past each row. If so, we need
@@ -1162,61 +981,40 @@
*/
bool restoreGLRowLength = false;
- // in case we need a temporary, trimmed copy of the src pixels
- SkAutoSMalloc<128 * 128> tempStorage;
-
if (mipMapsStatus) {
*mipMapsStatus = (mipLevelCount > 1) ?
GrMipMapsStatus::kValid : GrMipMapsStatus::kNotAllocated;
}
- if (mipLevelCount) {
- GR_GL_CALL(interface, PixelStorei(GR_GL_UNPACK_ALIGNMENT, 1));
- }
+ GR_GL_CALL(interface, PixelStorei(GR_GL_UNPACK_ALIGNMENT, 1));
- bool succeeded = true;
- if (kNewTexture_UploadType == uploadType) {
- if (0 == left && 0 == top && texWidth == width && texHeight == height) {
- succeeded = allocate_and_populate_texture(
- textureFormat, *interface, caps, target, internalFormat,
- internalFormatForTexStorage, externalFormat, externalType, bpp, texels,
- mipLevelCount, width, height, &restoreGLRowLength, mipMapsStatus);
- } else {
- succeeded = false;
- }
- } else {
- for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
- if (!texels[currentMipLevel].fPixels) {
- if (mipMapsStatus) {
- *mipMapsStatus = GrMipMapsStatus::kDirty;
- }
- continue;
+ for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
+ if (!texels[currentMipLevel].fPixels) {
+ if (mipMapsStatus) {
+ *mipMapsStatus = GrMipMapsStatus::kDirty;
}
- int twoToTheMipLevel = 1 << currentMipLevel;
- const int currentWidth = SkTMax(1, width / twoToTheMipLevel);
- const int currentHeight = SkTMax(1, height / twoToTheMipLevel);
- const size_t trimRowBytes = currentWidth * bpp;
- const size_t rowBytes = texels[currentMipLevel].fRowBytes;
-
- if (caps.writePixelsRowBytesSupport() && rowBytes != trimRowBytes) {
- GrGLint rowLength = static_cast<GrGLint>(rowBytes / bpp);
- GR_GL_CALL(interface, PixelStorei(GR_GL_UNPACK_ROW_LENGTH, rowLength));
- restoreGLRowLength = true;
- }
-
- GL_CALL(TexSubImage2D(target,
- currentMipLevel,
- left, top,
- currentWidth,
- currentHeight,
- externalFormat, externalType,
- texels[currentMipLevel].fPixels));
+ continue;
}
+ int twoToTheMipLevel = 1 << currentMipLevel;
+ const int currentWidth = SkTMax(1, width / twoToTheMipLevel);
+ const int currentHeight = SkTMax(1, height / twoToTheMipLevel);
+ const size_t trimRowBytes = currentWidth * bpp;
+ const size_t rowBytes = texels[currentMipLevel].fRowBytes;
+
+ if (caps.writePixelsRowBytesSupport() && (rowBytes != trimRowBytes || restoreGLRowLength)) {
+ GrGLint rowLength = static_cast<GrGLint>(rowBytes / bpp);
+ GR_GL_CALL(interface, PixelStorei(GR_GL_UNPACK_ROW_LENGTH, rowLength));
+ restoreGLRowLength = true;
+ }
+
+ GL_CALL(TexSubImage2D(target, currentMipLevel, left, top, currentWidth, currentHeight,
+ externalFormat, externalType, texels[currentMipLevel].fPixels));
}
-
- restore_pixelstore_state(*interface, caps, restoreGLRowLength);
-
- return succeeded;
+ if (restoreGLRowLength) {
+ SkASSERT(caps.writePixelsRowBytesSupport());
+ GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
+ }
+ return true;
}
bool GrGLGpu::uploadCompressedTexData(GrGLFormat format,
@@ -1228,7 +1026,7 @@
const GrGLCaps& caps = this->glCaps();
// We only need the internal format for compressed 2D textures.
- GrGLenum internalFormat = caps.getTexImageInternalFormat(format);
+ GrGLenum internalFormat = caps.getTexImageOrStorageInternalFormat(format);
if (!internalFormat) {
return 0;
}
@@ -1427,16 +1225,17 @@
int renderTargetSampleCnt,
SkBudgeted budgeted,
GrProtected isProtected,
- const GrMipLevel texels[],
- int mipLevelCount) {
+ int mipLevelCount,
+ uint32_t levelClearMask) {
// We don't support protected textures in GL.
if (isProtected == GrProtected::kYes) {
return nullptr;
}
SkASSERT(GrGLCaps::kNone_MSFBOType != this->glCaps().msFBOType() || renderTargetSampleCnt == 1);
-
- GrMipMapsStatus mipMapsStatus;
+ SkASSERT(mipLevelCount > 0);
+ GrMipMapsStatus mipMapsStatus =
+ mipLevelCount > 1 ? GrMipMapsStatus::kDirty : GrMipMapsStatus::kNotAllocated;
GrGLTextureParameters::SamplerOverriddenState initialState;
GrGLTexture::Desc texDesc;
texDesc.fSize = {desc.fWidth, desc.fHeight};
@@ -1447,18 +1246,8 @@
SkASSERT(texDesc.fFormat != GrGLFormat::kUnknown);
SkASSERT(!GrGLFormatIsCompressed(texDesc.fFormat));
- // TODO: Take these as parameters.
- auto textureColorType = GrPixelConfigToColorType(desc.fConfig);
- auto srcColorType = GrPixelConfigToColorType(desc.fConfig);
- texDesc.fID = this->createTexture2D({desc.fWidth, desc.fHeight},
- texDesc.fFormat,
- renderable,
- &initialState,
- textureColorType,
- srcColorType,
- texels,
- mipLevelCount,
- &mipMapsStatus);
+ texDesc.fID = this->createTexture2D({desc.fWidth, desc.fHeight}, texDesc.fFormat, renderable,
+ &initialState, mipLevelCount);
if (!texDesc.fID) {
return return_null_texture();
@@ -1483,16 +1272,49 @@
// The non-sampler params are still at their default values.
tex->parameters()->set(&initialState, GrGLTextureParameters::NonsamplerState(),
fResetTimestampForTextureParameters);
- bool clearLevelsWithoutData =
- this->caps()->shouldInitializeTextures() && this->glCaps().clearTextureSupport();
-
- if (clearLevelsWithoutData) {
- static constexpr uint32_t kZero = 0;
- int levelCnt = SkTMax(1, tex->texturePriv().maxMipMapLevel());
- for (int i = 0; i < levelCnt; ++i) {
- if (i >= mipLevelCount || !texels[i].fPixels) {
- GL_CALL(ClearTexImage(tex->textureID(), i, GR_GL_RGBA, GR_GL_UNSIGNED_BYTE,
- &kZero));
+ if (levelClearMask) {
+ GrGLenum externalFormat, externalType;
+ size_t bpp;
+ this->glCaps().getTexSubImageZeroFormatTypeAndBpp(texDesc.fFormat, &externalFormat,
+ &externalType, &bpp);
+ if (this->glCaps().clearTextureSupport()) {
+ for (int i = 0; i < mipLevelCount; ++i) {
+ if (levelClearMask & (1U << i)) {
+ GL_CALL(ClearTexImage(tex->textureID(), i, externalFormat, externalType,
+ nullptr));
+ }
+ }
+ } else if (this->glCaps().canFormatBeFBOColorAttachment(format.asGLFormat()) &&
+ !this->glCaps().performColorClearsAsDraws()) {
+ this->disableScissor();
+ this->disableWindowRectangles();
+ this->flushColorWrite(true);
+ this->flushClearColor(0, 0, 0, 0);
+ for (int i = 0; i < mipLevelCount; ++i) {
+ if (levelClearMask & (1U << i)) {
+ this->bindSurfaceFBOForPixelOps(tex.get(), i, GR_GL_FRAMEBUFFER,
+ kDst_TempFBOTarget);
+ GL_CALL(Clear(GR_GL_COLOR_BUFFER_BIT));
+ this->unbindSurfaceFBOForPixelOps(tex.get(), i, GR_GL_FRAMEBUFFER);
+ }
+ }
+ } else {
+ std::unique_ptr<char[]> zeros;
+ GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, 1));
+ for (int i = 0; i < mipLevelCount; ++i) {
+ if (levelClearMask & (1U << i)) {
+ int levelWidth = SkTMax(1, texDesc.fSize.width() >> i);
+ int levelHeight = SkTMax(1, texDesc.fSize.height() >> i);
+ // Levels only get smaller as we proceed. Once we create a zeros use it for all
+ // smaller levels that need clearing.
+ if (!zeros) {
+ size_t size = levelWidth * levelHeight * bpp;
+ zeros.reset(new char[size]());
+ }
+ this->bindTextureToScratchUnit(GR_GL_TEXTURE_2D, tex->textureID());
+ GL_CALL(TexSubImage2D(GR_GL_TEXTURE_2D, i, 0, 0, levelWidth, levelHeight,
+ externalFormat, externalType, zeros.get()));
+ }
}
}
}
@@ -1556,46 +1378,11 @@
// Default to unsupported, set this if we find a stencil format that works.
int firstWorkingStencilFormatIndex = -1;
- // Create color texture
- GrGLuint colorID = 0;
- GL_CALL(GenTextures(1, &colorID));
- this->bindTextureToScratchUnit(GR_GL_TEXTURE_2D, colorID);
- GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
- GR_GL_TEXTURE_MAG_FILTER,
- GR_GL_NEAREST));
- GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
- GR_GL_TEXTURE_MIN_FILTER,
- GR_GL_NEAREST));
- GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
- GR_GL_TEXTURE_WRAP_S,
- GR_GL_CLAMP_TO_EDGE));
- GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
- GR_GL_TEXTURE_WRAP_T,
- GR_GL_CLAMP_TO_EDGE));
-
- GrGLenum internalFormat = this->glCaps().getTexImageInternalFormat(format);
- GrGLenum externalFormat = this->glCaps().getBaseInternalFormat(format);
- GrGLenum externalType = this->glCaps().getFormatDefaultExternalType(format);
- if (!internalFormat || !externalFormat || !externalType) {
+ GrGLuint colorID =
+ this->createTexture2D({kSize, kSize}, format, GrRenderable::kYes, nullptr, 1);
+ if (!colorID) {
return -1;
}
-
- this->unbindCpuToGpuXferBuffer();
- CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
- GL_ALLOC_CALL(this->glInterface(), TexImage2D(GR_GL_TEXTURE_2D,
- 0,
- internalFormat,
- kSize,
- kSize,
- 0,
- externalFormat,
- externalType,
- nullptr));
- if (GR_GL_NO_ERROR != CHECK_ALLOC_ERROR(this->glInterface())) {
- GL_CALL(DeleteTextures(1, &colorID));
- return -1;
- }
-
// unbind the texture from the texture unit before binding it to the frame buffer
GL_CALL(BindTexture(GR_GL_TEXTURE_2D, 0));
@@ -1691,11 +1478,7 @@
GrGLFormat format,
GrRenderable renderable,
GrGLTextureParameters::SamplerOverriddenState* initialState,
- GrColorType textureColorType,
- GrColorType srcColorType,
- const GrMipLevel texels[],
- int mipLevelCount,
- GrMipMapsStatus* mipMapsStatus) {
+ int mipLevelCount) {
SkASSERT(format != GrGLFormat::kUnknown);
SkASSERT(!GrGLFormatIsCompressed(format));
@@ -1713,25 +1496,46 @@
GL_CALL(TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_USAGE, GR_GL_FRAMEBUFFER_ATTACHMENT));
}
- *initialState = set_initial_texture_params(this->glInterface(), GR_GL_TEXTURE_2D);
-
- if (!this->uploadTexData(format,
- textureColorType,
- size.width(), size.height(),
- GR_GL_TEXTURE_2D,
- kNewTexture_UploadType,
- 0,
- 0,
- size.width(),
- size.height(),
- srcColorType,
- texels,
- mipLevelCount,
- mipMapsStatus)) {
- GL_CALL(DeleteTextures(1, &id));
- return 0;
+ if (initialState) {
+ *initialState = set_initial_texture_params(this->glInterface(), GR_GL_TEXTURE_2D);
+ } else {
+ set_initial_texture_params(this->glInterface(), GR_GL_TEXTURE_2D);
}
- return id;
+
+ GrGLenum internalFormat = this->glCaps().getTexImageOrStorageInternalFormat(format);
+
+ bool success = false;
+ if (internalFormat) {
+ CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+ if (this->glCaps().formatSupportsTexStorage(format)) {
+ GL_ALLOC_CALL(this->glInterface(),
+ TexStorage2D(GR_GL_TEXTURE_2D, SkTMax(mipLevelCount, 1), internalFormat,
+ size.width(), size.height()));
+ success = (GR_GL_NO_ERROR == CHECK_ALLOC_ERROR(this->glInterface()));
+ } else {
+ GrGLenum externalFormat, externalType;
+ this->glCaps().getTexImageExternalFormatAndType(format, &externalFormat, &externalType);
+ GrGLenum error = GR_GL_NO_ERROR;
+ if (externalFormat && externalType) {
+ for (int level = 0; level < mipLevelCount && error == GR_GL_NO_ERROR; level++) {
+ const int twoToTheMipLevel = 1 << level;
+ const int currentWidth = SkTMax(1, size.width() / twoToTheMipLevel);
+ const int currentHeight = SkTMax(1, size.height() / twoToTheMipLevel);
+ GL_ALLOC_CALL(
+ this->glInterface(),
+ TexImage2D(GR_GL_TEXTURE_2D, level, internalFormat, currentWidth,
+ currentHeight, 0, externalFormat, externalType, nullptr));
+ error = CHECK_ALLOC_ERROR(this->glInterface());
+ }
+ success = (GR_GL_NO_ERROR == error);
+ }
+ }
+ }
+ if (success) {
+ return id;
+ }
+ GL_CALL(DeleteTextures(1, &id));
+ return 0;
}
GrStencilAttachment* GrGLGpu::createStencilAttachmentForRenderTarget(
@@ -2161,7 +1965,7 @@
}
} else {
// Use a temporary FBO.
- this->bindSurfaceFBOForPixelOps(surface, GR_GL_FRAMEBUFFER, kSrc_TempFBOTarget);
+ this->bindSurfaceFBOForPixelOps(surface, 0, GR_GL_FRAMEBUFFER, kSrc_TempFBOTarget);
fHWBoundRenderTargetUniqueID.makeInvalid();
}
@@ -2202,7 +2006,7 @@
}
if (!renderTarget) {
- this->unbindTextureFBOForPixelOps(GR_GL_FRAMEBUFFER, surface);
+ this->unbindSurfaceFBOForPixelOps(surface, 0, GR_GL_FRAMEBUFFER);
}
return true;
}
@@ -3015,10 +2819,10 @@
}
// If a temporary FBO was created, its non-zero ID is returned.
-void GrGLGpu::bindSurfaceFBOForPixelOps(GrSurface* surface, GrGLenum fboTarget,
+void GrGLGpu::bindSurfaceFBOForPixelOps(GrSurface* surface, int mipLevel, GrGLenum fboTarget,
TempFBOTarget tempFBOTarget) {
GrGLRenderTarget* rt = static_cast<GrGLRenderTarget*>(surface->asRenderTarget());
- if (!rt) {
+ if (!rt || mipLevel > 0) {
SkASSERT(surface->asTexture());
GrGLTexture* texture = static_cast<GrGLTexture*>(surface->asTexture());
GrGLuint texID = texture->textureID();
@@ -3031,20 +2835,20 @@
}
this->bindFramebuffer(fboTarget, *tempFBOID);
- GR_GL_CALL(this->glInterface(), FramebufferTexture2D(fboTarget,
- GR_GL_COLOR_ATTACHMENT0,
- target,
- texID,
- 0));
- texture->baseLevelWasBoundToFBO();
+ GR_GL_CALL(
+ this->glInterface(),
+ FramebufferTexture2D(fboTarget, GR_GL_COLOR_ATTACHMENT0, target, texID, mipLevel));
+ if (mipLevel == 0) {
+ texture->baseLevelWasBoundToFBO();
+ }
} else {
this->bindFramebuffer(fboTarget, rt->renderFBOID());
}
}
-void GrGLGpu::unbindTextureFBOForPixelOps(GrGLenum fboTarget, GrSurface* surface) {
+void GrGLGpu::unbindSurfaceFBOForPixelOps(GrSurface* surface, int mipLevel, GrGLenum fboTarget) {
// bindSurfaceFBOForPixelOps temporarily binds textures that are not render targets to
- if (!surface->asRenderTarget()) {
+ if (mipLevel > 0 || !surface->asRenderTarget()) {
SkASSERT(surface->asTexture());
GrGLenum textureTarget = static_cast<GrGLTexture*>(surface->asTexture())->target();
GR_GL_CALL(this->glInterface(), FramebufferTexture2D(fboTarget,
@@ -3426,7 +3230,7 @@
int h = srcRect.height();
// We don't swizzle at all in our copies.
this->bindTexture(0, GrSamplerState::ClampNearest(), GrSwizzle::RGBA(), srcTex);
- this->bindSurfaceFBOForPixelOps(dst, GR_GL_FRAMEBUFFER, kDst_TempFBOTarget);
+ this->bindSurfaceFBOForPixelOps(dst, 0, GR_GL_FRAMEBUFFER, kDst_TempFBOTarget);
this->flushViewport(dst->width(), dst->height());
fHWBoundRenderTargetUniqueID.makeInvalid();
SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, w, h);
@@ -3469,7 +3273,7 @@
this->flushFramebufferSRGB(true);
}
GL_CALL(DrawArrays(GR_GL_TRIANGLE_STRIP, 0, 4));
- this->unbindTextureFBOForPixelOps(GR_GL_FRAMEBUFFER, dst);
+ this->unbindSurfaceFBOForPixelOps(dst, 0, GR_GL_FRAMEBUFFER);
// The rect is already in device space so we pass in kTopLeft so no flip is done.
this->didWriteToSurface(dst, kTopLeft_GrSurfaceOrigin, &dstRect);
return true;
@@ -3478,7 +3282,7 @@
void GrGLGpu::copySurfaceAsCopyTexSubImage(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
const SkIPoint& dstPoint) {
SkASSERT(can_copy_texsubimage(dst, src, this->glCaps()));
- this->bindSurfaceFBOForPixelOps(src, GR_GL_FRAMEBUFFER, kSrc_TempFBOTarget);
+ this->bindSurfaceFBOForPixelOps(src, 0, GR_GL_FRAMEBUFFER, kSrc_TempFBOTarget);
GrGLTexture* dstTex = static_cast<GrGLTexture *>(dst->asTexture());
SkASSERT(dstTex);
// We modified the bound FBO
@@ -3489,7 +3293,7 @@
dstPoint.fX, dstPoint.fY,
srcRect.fLeft, srcRect.fTop,
srcRect.width(), srcRect.height()));
- this->unbindTextureFBOForPixelOps(GR_GL_FRAMEBUFFER, src);
+ this->unbindSurfaceFBOForPixelOps(src, 0, GR_GL_FRAMEBUFFER);
SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY,
srcRect.width(), srcRect.height());
// The rect is already in device space so we pass in kTopLeft so no flip is done.
@@ -3507,8 +3311,8 @@
}
}
- this->bindSurfaceFBOForPixelOps(dst, GR_GL_DRAW_FRAMEBUFFER, kDst_TempFBOTarget);
- this->bindSurfaceFBOForPixelOps(src, GR_GL_READ_FRAMEBUFFER, kSrc_TempFBOTarget);
+ this->bindSurfaceFBOForPixelOps(dst, 0, GR_GL_DRAW_FRAMEBUFFER, kDst_TempFBOTarget);
+ this->bindSurfaceFBOForPixelOps(src, 0, GR_GL_READ_FRAMEBUFFER, kSrc_TempFBOTarget);
// We modified the bound FBO
fHWBoundRenderTargetUniqueID.makeInvalid();
@@ -3525,8 +3329,8 @@
dstRect.fRight,
dstRect.fBottom,
GR_GL_COLOR_BUFFER_BIT, GR_GL_NEAREST));
- this->unbindTextureFBOForPixelOps(GR_GL_DRAW_FRAMEBUFFER, dst);
- this->unbindTextureFBOForPixelOps(GR_GL_READ_FRAMEBUFFER, src);
+ this->unbindSurfaceFBOForPixelOps(dst, 0, GR_GL_DRAW_FRAMEBUFFER);
+ this->unbindSurfaceFBOForPixelOps(src, 0, GR_GL_READ_FRAMEBUFFER);
// The rect is already in device space so we pass in kTopLeft so no flip is done.
this->didWriteToSurface(dst, kTopLeft_GrSurfaceOrigin, &dstRect);
@@ -3764,8 +3568,7 @@
GrGLTextureInfo info;
GrGLTextureParameters::SamplerOverriddenState initialState;
- int mipLevelCount = 0;
- SkAutoTMalloc<GrMipLevel> texels;
+ SkTDArray<GrMipLevel> texels;
SkAutoMalloc pixelStorage;
SkImage::CompressionType compressionType;
if (GrGLFormatToCompressionType(glFormat, &compressionType)) {
@@ -3791,16 +3594,15 @@
info.fTarget = GR_GL_TEXTURE_2D;
} else {
if (srcPixels) {
- mipLevelCount = 1;
- texels.reset(mipLevelCount);
- texels.get()[0] = {srcPixels, rowBytes};
+ texels.append(1);
+ texels[0] = {srcPixels, rowBytes};
} else if (color) {
- mipLevelCount = 1;
+ int mipLevelCount = 1;
if (GrMipMapped::kYes == mipMapped) {
mipLevelCount = SkMipMap::ComputeLevelCount(w, h) + 1;
}
- texels.reset(mipLevelCount);
+ texels.append(mipLevelCount);
SkTArray<size_t> individualMipOffsets(mipLevelCount);
size_t bytesPerPixel = GrBytesPerPixel(config);
@@ -3817,7 +3619,7 @@
int twoToTheMipLevel = 1 << i;
int currentWidth = SkTMax(1, w / twoToTheMipLevel);
- texels.get()[i] = {&(tmpPixels[offset]), currentWidth * bytesPerPixel};
+ texels[i] = {&(tmpPixels[offset]), currentWidth * bytesPerPixel};
}
}
GrSurfaceDesc desc;
@@ -3827,20 +3629,19 @@
info.fTarget = GR_GL_TEXTURE_2D;
info.fFormat = GrGLFormatToEnum(glFormat);
- // TODO: Take these as parameters.
- auto srcColorType = GrPixelConfigToColorType(desc.fConfig);
- info.fID = this->createTexture2D({desc.fWidth, desc.fHeight},
- glFormat,
- renderable,
- &initialState,
- textureColorType,
- srcColorType,
- texels,
- mipLevelCount,
- nullptr);
+ info.fID = this->createTexture2D({desc.fWidth, desc.fHeight}, glFormat, renderable,
+ &initialState, SkTMax(1, texels.count()));
if (!info.fID) {
return GrBackendTexture(); // invalid
}
+ auto srcColorType = GrPixelConfigToColorType(desc.fConfig);
+ if (!texels.empty() &&
+ !this->uploadTexData(glFormat, textureColorType, desc.fWidth, desc.fHeight,
+ GR_GL_TEXTURE_2D, 0, 0, desc.fWidth, desc.fHeight, srcColorType,
+ texels.begin(), texels.count())) {
+ GL_CALL(DeleteTextures(1, &info.fID));
+ return GrBackendTexture();
+ }
}
// unbind the texture from the texture unit to avoid asserts
@@ -3888,17 +3689,7 @@
if (!this->glCaps().isFormatRenderable(format, 1)) {
return {};
}
- bool useTexture = false;
- GrGLenum colorBufferFormat;
- GrGLenum externalFormat = 0, externalType = 0;
- if (format == GrGLFormat::kBGRA8) {
- // BGRA render buffers are not supported.
- this->glCaps().getTexImageFormats(format, colorType, colorType, &colorBufferFormat,
- &externalFormat, &externalType);
- useTexture = true;
- } else {
- colorBufferFormat = this->glCaps().getRenderbufferInternalFormat(format);
- }
+ bool useTexture = format == GrGLFormat::kBGRA8;
int sFormatIdx = this->getCompatibleStencilIndex(format);
if (sFormatIdx < 0) {
return {};
@@ -3931,7 +3722,7 @@
GrGLFramebufferInfo info;
info.fFBOID = 0;
- info.fFormat = this->glCaps().formatSizedInternalFormat(format);
+ info.fFormat = GrGLFormatToEnum(format);
GL_CALL(GenFramebuffers(1, &info.fFBOID));
if (!info.fFBOID) {
deleteIDs();
@@ -3942,15 +3733,19 @@
this->bindFramebuffer(GR_GL_FRAMEBUFFER, info.fFBOID);
if (useTexture) {
- this->bindTextureToScratchUnit(GR_GL_TEXTURE_2D, colorID);
- GL_CALL(TexImage2D(GR_GL_TEXTURE_2D, 0, colorBufferFormat, w, h, 0, externalFormat,
- externalType, nullptr));
+ GrGLTextureParameters::SamplerOverriddenState initialState;
+ colorID = this->createTexture2D({w, h}, format, GrRenderable::kYes, &initialState, 1);
+ if (!colorID) {
+ deleteIDs();
+ return {};
+ }
GL_CALL(FramebufferTexture2D(GR_GL_FRAMEBUFFER, GR_GL_COLOR_ATTACHMENT0, GR_GL_TEXTURE_2D,
colorID, 0));
} else {
+ GrGLenum renderBufferFormat = this->glCaps().getRenderbufferInternalFormat(format);
GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER, colorID));
GL_ALLOC_CALL(this->glInterface(),
- RenderbufferStorage(GR_GL_RENDERBUFFER, colorBufferFormat, w, h));
+ RenderbufferStorage(GR_GL_RENDERBUFFER, renderBufferFormat, w, h));
GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, GR_GL_COLOR_ATTACHMENT0,
GR_GL_RENDERBUFFER, colorID));
}