From 900bd4a0463bc6471ef07a77120b413bd8f472b2 Mon Sep 17 00:00:00 2001 From: jvanverth Date: Fri, 29 Apr 2016 13:53:12 -0700 Subject: Add mipmap loading to Vulkan. GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1925303002 Review-Url: https://codereview.chromium.org/1925303002 --- src/gpu/GrGpu.cpp | 19 +++ src/gpu/GrGpu.h | 3 + src/gpu/gl/GrGLGpu.cpp | 27 +--- src/gpu/gl/GrGLGpu.h | 2 - src/gpu/vk/GrVkGpu.cpp | 392 +++++++++++++++++++++++++++------------------ src/gpu/vk/GrVkGpu.h | 16 +- src/gpu/vk/GrVkImage.h | 21 ++- src/gpu/vk/GrVkTexture.cpp | 44 +++-- src/gpu/vk/GrVkTexture.h | 7 +- 9 files changed, 310 insertions(+), 221 deletions(-) diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp index eef7118a1b..691c0ab83a 100644 --- a/src/gpu/GrGpu.cpp +++ b/src/gpu/GrGpu.cpp @@ -20,6 +20,7 @@ #include "GrRenderTargetPriv.h" #include "GrStencilAttachment.h" #include "GrSurfacePriv.h" +#include "GrTexturePriv.h" #include "SkTypes.h" GrMesh& GrMesh::operator =(const GrMesh& di) { @@ -375,6 +376,8 @@ bool GrGpu::writePixels(GrSurface* surface, this->handleDirtyContext(); if (this->onWritePixels(surface, left, top, width, height, config, texels)) { + SkIRect rect = SkIRect::MakeXYWH(left, top, width, height); + this->didWriteToSurface(surface, &rect, texels.count()); fStats.incTextureUploads(); return true; } @@ -403,6 +406,8 @@ bool GrGpu::transferPixels(GrSurface* surface, this->handleDirtyContext(); if (this->onTransferPixels(surface, left, top, width, height, config, transferBuffer, offset, rowBytes)) { + SkIRect rect = SkIRect::MakeXYWH(left, top, width, height); + this->didWriteToSurface(surface, &rect); fStats.incTransfersToTexture(); return true; } @@ -415,6 +420,20 @@ void GrGpu::resolveRenderTarget(GrRenderTarget* target) { this->onResolveRenderTarget(target); } +void GrGpu::didWriteToSurface(GrSurface* surface, const SkIRect* bounds, uint32_t mipLevels) const { + SkASSERT(surface); + // Mark any MIP chain and resolve buffer as dirty if and only if there is a non-empty bounds. + if (nullptr == bounds || !bounds->isEmpty()) { + if (GrRenderTarget* target = surface->asRenderTarget()) { + target->flagAsNeedingResolve(bounds); + } + GrTexture* texture = surface->asTexture(); + if (texture && 1 == mipLevels) { + texture->texturePriv().dirtyMipMaps(true); + } + } +} + inline static uint8_t multisample_specs_id(uint8_t numSamples, GrSurfaceOrigin origin, const GrCaps& caps) { if (!caps.sampleLocationsSupport()) { diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h index 97ca11ca07..e38b913a22 100644 --- a/src/gpu/GrGpu.h +++ b/src/gpu/GrGpu.h @@ -512,6 +512,9 @@ protected: } } + // Handles cases where a surface will be updated without a call to flushRenderTarget + void didWriteToSurface(GrSurface* surface, const SkIRect* bounds, uint32_t mipLevels = 1) const; + Stats fStats; SkAutoTDelete fPathRendering; // Subclass must initialize this in its constructor. diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp index 2fe9c023b2..9d2984f186 100644 --- a/src/gpu/gl/GrGLGpu.cpp +++ b/src/gpu/gl/GrGLGpu.cpp @@ -879,11 +879,6 @@ bool GrGLGpu::onWritePixels(GrSurface* surface, left, top, width, height, config, texels); } - if (success) { - SkIRect rect = SkIRect::MakeXYWH(left, top, width, height); - this->didWriteToSurface(surface, &rect, texels.count()); - } - return success; } @@ -918,13 +913,7 @@ bool GrGLGpu::onTransferPixels(GrSurface* surface, texels.push_back(mipLevel); success = this->uploadTexData(glTex->desc(), glTex->target(), kTransfer_UploadType, left, top, width, height, config, texels); - if (success) { - SkIRect rect = SkIRect::MakeXYWH(left, top, width, height); - this->didWriteToSurface(surface, &rect); - return true; - } - - return false; + return success; } // For GL_[UN]PACK_ALIGNMENT. @@ -2671,20 +2660,6 @@ void GrGLGpu::flushViewport(const GrGLIRect& viewport) { } } -void GrGLGpu::didWriteToSurface(GrSurface* surface, const SkIRect* bounds, int mipLevels) const { - SkASSERT(surface); - // Mark any MIP chain and resolve buffer as dirty if and only if there is a non-empty bounds. - if (nullptr == bounds || !bounds->isEmpty()) { - if (GrRenderTarget* target = surface->asRenderTarget()) { - target->flagAsNeedingResolve(bounds); - } - GrTexture* texture = surface->asTexture(); - if (texture && 1 == mipLevels) { - texture->texturePriv().dirtyMipMaps(true); - } - } -} - GrGLenum gPrimitiveType2GLMode[] = { GR_GL_TRIANGLES, GR_GL_TRIANGLE_STRIP, diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h index 5f3a751fee..d6af67947a 100644 --- a/src/gpu/gl/GrGLGpu.h +++ b/src/gpu/gl/GrGLGpu.h @@ -308,8 +308,6 @@ private: // bounds is region that may be modified. // nullptr means whole target. Can be an empty rect. void flushRenderTarget(GrGLRenderTarget*, const SkIRect* bounds, bool disableSRGB = false); - // Handles cases where a surface will be updated without a call to flushRenderTarget - void didWriteToSurface(GrSurface*, const SkIRect* bounds, int mipLevels = 1) const; // Need not be called if flushRenderTarget is used. void flushViewport(const GrGLIRect&); diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp index 20062b7631..93eb4a8e0c 100644 --- a/src/gpu/vk/GrVkGpu.cpp +++ b/src/gpu/vk/GrVkGpu.cpp @@ -30,6 +30,7 @@ #include "GrVkVertexBuffer.h" #include "SkConfig8888.h" +#include "SkMipMap.h" #include "vk/GrVkInterface.h" #include "vk/GrVkTypes.h" @@ -237,7 +238,7 @@ bool GrVkGpu::onWritePixels(GrSurface* surface, return false; } - // TODO: We're ignoring MIP levels here. + // Make sure we have at least the base level if (texels.empty() || !texels.begin()->fPixels) { return false; } @@ -259,45 +260,53 @@ bool GrVkGpu::onWritePixels(GrSurface* surface, // height); } else { bool linearTiling = vkTex->isLinearTiled(); - if (linearTiling && VK_IMAGE_LAYOUT_PREINITIALIZED != vkTex->currentLayout()) { - // Need to change the layout to general in order to perform a host write - VkImageLayout layout = vkTex->currentLayout(); - VkPipelineStageFlags srcStageMask = GrVkMemory::LayoutToPipelineStageFlags(layout); - VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_HOST_BIT; - VkAccessFlags srcAccessMask = GrVkMemory::LayoutToSrcAccessMask(layout); - VkAccessFlags dstAccessMask = VK_ACCESS_HOST_WRITE_BIT; - vkTex->setImageLayout(this, - VK_IMAGE_LAYOUT_GENERAL, - srcAccessMask, - dstAccessMask, - srcStageMask, - dstStageMask, - false); + if (linearTiling) { + if (texels.count() > 1) { + SkDebugf("Can't upload mipmap data to linear tiled texture"); + return false; + } + if (VK_IMAGE_LAYOUT_PREINITIALIZED != vkTex->currentLayout()) { + // Need to change the layout to general in order to perform a host write + VkImageLayout layout = vkTex->currentLayout(); + VkPipelineStageFlags srcStageMask = GrVkMemory::LayoutToPipelineStageFlags(layout); + VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_HOST_BIT; + VkAccessFlags srcAccessMask = GrVkMemory::LayoutToSrcAccessMask(layout); + VkAccessFlags dstAccessMask = VK_ACCESS_HOST_WRITE_BIT; + vkTex->setImageLayout(this, + VK_IMAGE_LAYOUT_GENERAL, + srcAccessMask, + dstAccessMask, + srcStageMask, + dstStageMask, + false); + } + success = this->uploadTexDataLinear(vkTex, left, top, width, height, config, + texels.begin()->fPixels, texels.begin()->fRowBytes); + } else { + uint32_t mipLevels = texels.count(); + if (vkTex->texturePriv().maxMipMapLevel() != mipLevels) { + if (!vkTex->reallocForMipmap(this, mipLevels)) { + return false; + } + } + success = this->uploadTexDataOptimal(vkTex, left, top, width, height, config, texels); } - success = this->uploadTexData(vkTex, left, top, width, height, config, - texels.begin()->fPixels, texels.begin()->fRowBytes); } - - if (success) { - vkTex->texturePriv().dirtyMipMaps(true); - return true; - } - - return false; + + return success; } -bool GrVkGpu::uploadTexData(GrVkTexture* tex, - int left, int top, int width, int height, - GrPixelConfig dataConfig, - const void* data, - size_t rowBytes) { +bool GrVkGpu::uploadTexDataLinear(GrVkTexture* tex, + int left, int top, int width, int height, + GrPixelConfig dataConfig, + const void* data, + size_t rowBytes) { SkASSERT(data); + SkASSERT(tex->isLinearTiled()); // If we're uploading compressed data then we should be using uploadCompressedTexData SkASSERT(!GrPixelConfigIsCompressed(dataConfig)); - bool linearTiling = tex->isLinearTiled(); - size_t bpp = GrBytesPerPixel(dataConfig); const GrSurfaceDesc& desc = tex->desc(); @@ -308,133 +317,190 @@ bool GrVkGpu::uploadTexData(GrVkTexture* tex, } size_t trimRowBytes = width * bpp; - if (linearTiling) { - SkASSERT(VK_IMAGE_LAYOUT_PREINITIALIZED == tex->currentLayout() || - VK_IMAGE_LAYOUT_GENERAL == tex->currentLayout()); - const VkImageSubresource subres = { - VK_IMAGE_ASPECT_COLOR_BIT, - 0, // mipLevel - 0, // arraySlice - }; - VkSubresourceLayout layout; - VkResult err; - - const GrVkInterface* interface = this->vkInterface(); - - GR_VK_CALL(interface, GetImageSubresourceLayout(fDevice, - tex->textureImage(), - &subres, - &layout)); - - int texTop = kBottomLeft_GrSurfaceOrigin == desc.fOrigin ? tex->height() - top - height - : top; - VkDeviceSize offset = texTop*layout.rowPitch + left*bpp; - VkDeviceSize size = height*layout.rowPitch; - void* mapPtr; - err = GR_VK_CALL(interface, MapMemory(fDevice, tex->textureMemory(), offset, size, 0, - &mapPtr)); - if (err) { - return false; - } + SkASSERT(VK_IMAGE_LAYOUT_PREINITIALIZED == tex->currentLayout() || + VK_IMAGE_LAYOUT_GENERAL == tex->currentLayout()); + const VkImageSubresource subres = { + VK_IMAGE_ASPECT_COLOR_BIT, + 0, // mipLevel + 0, // arraySlice + }; + VkSubresourceLayout layout; + VkResult err; + + const GrVkInterface* interface = this->vkInterface(); + + GR_VK_CALL(interface, GetImageSubresourceLayout(fDevice, + tex->textureImage(), + &subres, + &layout)); + + int texTop = kBottomLeft_GrSurfaceOrigin == desc.fOrigin ? tex->height() - top - height : top; + VkDeviceSize offset = texTop*layout.rowPitch + left*bpp; + VkDeviceSize size = height*layout.rowPitch; + void* mapPtr; + err = GR_VK_CALL(interface, MapMemory(fDevice, tex->textureMemory(), offset, size, 0, + &mapPtr)); + if (err) { + return false; + } - if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) { - // copy into buffer by rows - const char* srcRow = reinterpret_cast(data); - char* dstRow = reinterpret_cast(mapPtr)+(height - 1)*layout.rowPitch; - for (int y = 0; y < height; y++) { - memcpy(dstRow, srcRow, trimRowBytes); - srcRow += rowBytes; - dstRow -= layout.rowPitch; - } + if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) { + // copy into buffer by rows + const char* srcRow = reinterpret_cast(data); + char* dstRow = reinterpret_cast(mapPtr)+(height - 1)*layout.rowPitch; + for (int y = 0; y < height; y++) { + memcpy(dstRow, srcRow, trimRowBytes); + srcRow += rowBytes; + dstRow -= layout.rowPitch; + } + } else { + // If there is no padding on the src (rowBytes) or dst (layout.rowPitch) we can memcpy + if (trimRowBytes == rowBytes && trimRowBytes == layout.rowPitch) { + memcpy(mapPtr, data, trimRowBytes * height); } else { - // If there is no padding on the src (rowBytes) or dst (layout.rowPitch) we can memcpy - if (trimRowBytes == rowBytes && trimRowBytes == layout.rowPitch) { - memcpy(mapPtr, data, trimRowBytes * height); - } else { - SkRectMemcpy(mapPtr, static_cast(layout.rowPitch), data, rowBytes, - trimRowBytes, height); - } + SkRectMemcpy(mapPtr, static_cast(layout.rowPitch), data, rowBytes, + trimRowBytes, height); } + } - GR_VK_CALL(interface, UnmapMemory(fDevice, tex->textureMemory())); - } else { - GrVkTransferBuffer* transferBuffer = - GrVkTransferBuffer::Create(this, trimRowBytes * height, GrVkBuffer::kCopyRead_Type); - - void* mapPtr = transferBuffer->map(); - - if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) { - // copy into buffer by rows - const char* srcRow = reinterpret_cast(data); - char* dstRow = reinterpret_cast(mapPtr)+(height - 1)*trimRowBytes; - for (int y = 0; y < height; y++) { - memcpy(dstRow, srcRow, trimRowBytes); - srcRow += rowBytes; - dstRow -= trimRowBytes; + GR_VK_CALL(interface, UnmapMemory(fDevice, tex->textureMemory())); + + return true; +} + +bool GrVkGpu::uploadTexDataOptimal(GrVkTexture* tex, + int left, int top, int width, int height, + GrPixelConfig dataConfig, + const SkTArray& texels) { + SkASSERT(!tex->isLinearTiled()); + // The assumption is either that we have no mipmaps, or that our rect is the entire texture + SkASSERT(1 == texels.count() || + (0 == left && 0 == top && width == tex->width() && height == tex->height())); + + // If we're uploading compressed data then we should be using uploadCompressedTexData + SkASSERT(!GrPixelConfigIsCompressed(dataConfig)); + + if (width == 0 || height == 0) { + return false; + } + + const GrSurfaceDesc& desc = tex->desc(); + SkASSERT(this->caps()->isConfigTexturable(desc.fConfig)); + size_t bpp = GrBytesPerPixel(dataConfig); + + // texels is const. + // But we may need to adjust the fPixels ptr based on the copyRect. + // In this case we need to make a non-const shallow copy of texels. + const SkTArray* texelsPtr = &texels; + SkTArray texelsCopy; + if (0 != left || 0 != top || width != tex->width() || height != tex->height()) { + texelsCopy = texels; + + SkASSERT(1 == texels.count()); + SkASSERT(texelsCopy[0].fPixels); + + if (!GrSurfacePriv::AdjustWritePixelParams(desc.fWidth, desc.fHeight, bpp, &left, &top, + &width, &height, &texelsCopy[0].fPixels, + &texelsCopy[0].fRowBytes)) { + return false; + } + + texelsPtr = &texelsCopy; + } + + // Determine whether we need to flip when we copy into the buffer + bool flipY = (kBottomLeft_GrSurfaceOrigin == desc.fOrigin && !texelsPtr->empty()); + + // find the combined size of all the mip levels and the relative offset of + // each into the collective buffer + size_t combinedBufferSize = 0; + SkTArray individualMipOffsets(texelsPtr->count()); + for (int currentMipLevel = 0; currentMipLevel < texelsPtr->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; + individualMipOffsets.push_back(combinedBufferSize); + combinedBufferSize += trimmedSize; + } + + // allocate buffer to hold our mip data + GrVkTransferBuffer* transferBuffer = + GrVkTransferBuffer::Create(this, combinedBufferSize, GrVkBuffer::kCopyRead_Type); + + char* buffer = (char*) transferBuffer->map(); + SkTArray regions(texelsPtr->count()); + + for (int currentMipLevel = 0; currentMipLevel < texelsPtr->count(); currentMipLevel++) { + int twoToTheMipLevel = 1 << currentMipLevel; + int currentWidth = SkTMax(1, width / twoToTheMipLevel); + int currentHeight = SkTMax(1, height / twoToTheMipLevel); + const size_t trimRowBytes = currentWidth * bpp; + const size_t rowBytes = (*texelsPtr)[currentMipLevel].fRowBytes; + + // copy data into the buffer, skipping the trailing bytes + char* dst = buffer + individualMipOffsets[currentMipLevel]; + const char* src = (const char*)(*texelsPtr)[currentMipLevel].fPixels; + if (flipY) { + src += (currentHeight - 1) * rowBytes; + for (int y = 0; y < currentHeight; y++) { + memcpy(dst, src, trimRowBytes); + src -= rowBytes; + dst += trimRowBytes; } + } else if (trimRowBytes == rowBytes) { + memcpy(dst, src, trimRowBytes * currentHeight); } else { - // If there is no padding on the src data rows, we can do a single memcpy - if (trimRowBytes == rowBytes) { - memcpy(mapPtr, data, trimRowBytes * height); - } else { - SkRectMemcpy(mapPtr, trimRowBytes, data, rowBytes, trimRowBytes, height); - } + SkRectMemcpy(dst, trimRowBytes, src, rowBytes, trimRowBytes, currentHeight); } - transferBuffer->unmap(); - - // make sure the unmap has finished - transferBuffer->addMemoryBarrier(this, - VK_ACCESS_HOST_WRITE_BIT, - VK_ACCESS_TRANSFER_READ_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - false); - - // Set up copy region - bool flipY = kBottomLeft_GrSurfaceOrigin == tex->origin(); - VkOffset3D offset = { - left, - flipY ? tex->height() - top - height : top, - 0 - }; - - VkBufferImageCopy region; + VkBufferImageCopy& region = regions.push_back(); memset(®ion, 0, sizeof(VkBufferImageCopy)); - region.bufferOffset = 0; - region.bufferRowLength = width; - region.bufferImageHeight = height; - region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; - region.imageOffset = offset; - region.imageExtent = { (uint32_t)width, (uint32_t)height, 1 }; - - // Change layout of our target so it can be copied to - VkImageLayout layout = tex->currentLayout(); - VkPipelineStageFlags srcStageMask = GrVkMemory::LayoutToPipelineStageFlags(layout); - VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; - VkAccessFlags srcAccessMask = GrVkMemory::LayoutToSrcAccessMask(layout); - VkAccessFlags dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - tex->setImageLayout(this, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - srcAccessMask, - dstAccessMask, - srcStageMask, - dstStageMask, - false); + region.bufferOffset = individualMipOffsets[currentMipLevel]; + region.bufferRowLength = currentWidth; + region.bufferImageHeight = currentHeight; + region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, currentMipLevel, 0, 1 }; + region.imageOffset = { left, top, 0 }; + region.imageExtent = { (uint32_t)currentWidth, (uint32_t)currentHeight, 1 }; + } - // Copy the buffer to the image - fCurrentCmdBuffer->copyBufferToImage(this, - transferBuffer, - tex, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, - ®ion); + transferBuffer->unmap(); - // Submit the current command buffer to the Queue - this->submitCommandBuffer(kSkip_SyncQueue); + // make sure the unmap has finished + transferBuffer->addMemoryBarrier(this, + VK_ACCESS_HOST_WRITE_BIT, + VK_ACCESS_TRANSFER_READ_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + false); - transferBuffer->unref(); - } + // Change layout of our target so it can be copied to + VkImageLayout layout = tex->currentLayout(); + VkPipelineStageFlags srcStageMask = GrVkMemory::LayoutToPipelineStageFlags(layout); + VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + VkAccessFlags srcAccessMask = GrVkMemory::LayoutToSrcAccessMask(layout); + VkAccessFlags dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + // TODO: change layout of all the subresources + tex->setImageLayout(this, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + srcAccessMask, + dstAccessMask, + srcStageMask, + dstStageMask, + false); + + // Copy the buffer to the image + fCurrentCmdBuffer->copyBufferToImage(this, + transferBuffer, + tex, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + regions.count(), + regions.begin()); + + // Submit the current command buffer to the Queue + this->submitCommandBuffer(kSkip_SyncQueue); + + transferBuffer->unref(); return true; } @@ -455,6 +521,11 @@ GrTexture* GrVkGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budget bool linearTiling = false; if (SkToBool(desc.fFlags & kZeroCopy_GrSurfaceFlag)) { + // we can't have a linear texture with a mipmap + if (texels.count() > 1) { + SkDebugf("Trying to create linear tiled texture with mipmap"); + return nullptr; + } if (fVkCaps->isConfigTexurableLinearly(desc.fConfig) && (!renderTarget || fVkCaps->isConfigRenderableLinearly(desc.fConfig, false))) { linearTiling = true; @@ -487,7 +558,7 @@ GrTexture* GrVkGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budget imageDesc.fFormat = pixelFormat; imageDesc.fWidth = desc.fWidth; imageDesc.fHeight = desc.fHeight; - imageDesc.fLevels = 1; // TODO: support miplevels for optimal tiling + imageDesc.fLevels = linearTiling ? 1 : texels.count(); imageDesc.fSamples = 1; imageDesc.fImageTiling = linearTiling ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL; imageDesc.fUsageFlags = usageFlags; @@ -505,11 +576,17 @@ GrTexture* GrVkGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budget return nullptr; } - // TODO: We're ignoring MIP levels here. if (!texels.empty()) { SkASSERT(texels.begin()->fPixels); - if (!this->uploadTexData(tex, 0, 0, desc.fWidth, desc.fHeight, desc.fConfig, - texels.begin()->fPixels, texels.begin()->fRowBytes)) { + bool success; + if (linearTiling) { + success = this->uploadTexDataLinear(tex, 0, 0, desc.fWidth, desc.fHeight, desc.fConfig, + texels.begin()->fPixels, texels.begin()->fRowBytes); + } else { + success = this->uploadTexDataOptimal(tex, 0, 0, desc.fWidth, desc.fHeight, desc.fConfig, + texels); + } + if (!success) { tex->unref(); return nullptr; } @@ -609,8 +686,9 @@ GrRenderTarget* GrVkGpu::onWrapBackendRenderTarget(const GrBackendRenderTargetDe } void GrVkGpu::generateMipmap(GrVkTexture* tex) const { - // don't need to do anything for linearly tiled textures (can't have mipmaps) + // don't do anything for linearly tiled textures (can't have mipmaps) if (tex->isLinearTiled()) { + SkDebugf("Trying to create mipmap for linear tiled texture"); return; } @@ -635,6 +713,7 @@ void GrVkGpu::generateMipmap(GrVkTexture* tex) const { VkAccessFlags srcAccessMask = GrVkMemory::LayoutToSrcAccessMask(origSrcLayout); VkAccessFlags dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + // TODO: change layout of all the subresources tex->setImageLayout(this, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, srcAccessMask, dstAccessMask, srcStageMask, dstStageMask, false); @@ -642,7 +721,8 @@ void GrVkGpu::generateMipmap(GrVkTexture* tex) const { const GrVkImage::Resource* oldResource = tex->resource(); oldResource->ref(); - if (!tex->reallocForMipmap(this)) { + uint32_t mipLevels = SkMipMap::ComputeLevelCount(tex->width(), tex->height()); + if (!tex->reallocForMipmap(this, mipLevels)) { oldResource->unref(this); return; } @@ -695,6 +775,8 @@ void GrVkGpu::generateMipmap(GrVkTexture* tex) const { blitRegion.dstOffsets[0] = { 0, 0, 0 }; blitRegion.dstOffsets[1] = { width/2, height/2, 0 }; + // TODO: insert image barrier to wait on previous blit + fCurrentCmdBuffer->blitImage(this, tex->resource(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, @@ -1276,6 +1358,10 @@ void GrVkGpu::copySurfaceAsCopyImage(GrSurface* dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region); + + SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, + srcRect.width(), srcRect.height()); + this->didWriteToSurface(dst, &dstRect); } inline bool can_copy_as_blit(const GrSurface* dst, @@ -1386,6 +1472,8 @@ void GrVkGpu::copySurfaceAsBlit(GrSurface* dst, 1, &blitRegion, VK_FILTER_NEAREST); // We never scale so any filter works here + + this->didWriteToSurface(dst, &dstRect); } inline bool can_copy_as_draw(const GrSurface* dst, diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h index 3f76fd2416..8b8883bd04 100644 --- a/src/gpu/vk/GrVkGpu.h +++ b/src/gpu/vk/GrVkGpu.h @@ -192,12 +192,16 @@ private: const SkIRect& srcRect, const SkIPoint& dstPoint); - // helper for onCreateTexture and writeTexturePixels - bool uploadTexData(GrVkTexture* tex, - int left, int top, int width, int height, - GrPixelConfig dataConfig, - const void* data, - size_t rowBytes); + // helpers for onCreateTexture and writeTexturePixels + bool uploadTexDataLinear(GrVkTexture* tex, + int left, int top, int width, int height, + GrPixelConfig dataConfig, + const void* data, + size_t rowBytes); + bool uploadTexDataOptimal(GrVkTexture* tex, + int left, int top, int width, int height, + GrPixelConfig dataConfig, + const SkTArray&); SkAutoTUnref fBackendContext; SkAutoTUnref fVkCaps; diff --git a/src/gpu/vk/GrVkImage.h b/src/gpu/vk/GrVkImage.h index 5b3b527202..d54f393641 100644 --- a/src/gpu/vk/GrVkImage.h +++ b/src/gpu/vk/GrVkImage.h @@ -9,6 +9,8 @@ #define GrVkImage_DEFINED #include "GrVkResource.h" + +#include "GrTypesPriv.h" #include "SkTypes.h" #include "vk/GrVkDefines.h" @@ -22,13 +24,14 @@ public: public: enum Flags { kNo_Flags = 0, - kLinearTiling_Flag = 0x01 + kLinearTiling_Flag = 0x01, + kBorrowed_Flag = 0x02 }; - VkImage fImage; - VkDeviceMemory fAlloc; - Flags fFlags; - VkFormat fFormat; + VkImage fImage; + VkDeviceMemory fAlloc; + VkFormat fFormat; + uint32_t fFlags; Resource() : INHERITED() @@ -37,10 +40,11 @@ public: , fFlags(kNo_Flags) , fFormat(VK_FORMAT_UNDEFINED) {} - Resource(VkImage image, VkDeviceMemory alloc, Flags flags, VkFormat format) + Resource(VkImage image, VkDeviceMemory alloc, uint32_t flags, VkFormat format) : fImage(image), fAlloc(alloc), fFlags(flags), fFormat(format) {} ~Resource() override {} + private: void freeGPUData(const GrVkGpu* gpu) const override; @@ -50,8 +54,9 @@ public: // for wrapped textures class BorrowedResource : public Resource { public: - BorrowedResource(VkImage image, VkDeviceMemory alloc, Flags flags, VkFormat format) - : Resource(image, alloc, flags, format) {} + BorrowedResource(VkImage image, VkDeviceMemory alloc, uint32_t flags, VkFormat format) + : Resource(image, alloc, (flags | kBorrowed_Flag), format) { + } private: void freeGPUData(const GrVkGpu* gpu) const override; }; diff --git a/src/gpu/vk/GrVkTexture.cpp b/src/gpu/vk/GrVkTexture.cpp index 9c68df705a..91852da0a5 100644 --- a/src/gpu/vk/GrVkTexture.cpp +++ b/src/gpu/vk/GrVkTexture.cpp @@ -10,7 +10,6 @@ #include "GrVkImageView.h" #include "GrTexturePriv.h" #include "GrVkUtil.h" -#include "SkMipMap.h" #include "vk/GrVkTypes.h" @@ -24,8 +23,7 @@ GrVkTexture::GrVkTexture(GrVkGpu* gpu, const GrVkImageView* view) : GrSurface(gpu, desc) , GrVkImage(imageResource) - , INHERITED(gpu, desc, kSampler2D_GrSLType, - false) // false because we don't upload MIP data in Vk yet + , INHERITED(gpu, desc, kSampler2D_GrSLType, desc.fIsMipMapped) , fTextureView(view) { this->registerWithCache(budgeted); } @@ -37,8 +35,7 @@ GrVkTexture::GrVkTexture(GrVkGpu* gpu, const GrVkImageView* view) : GrSurface(gpu, desc) , GrVkImage(imageResource) - , INHERITED(gpu, desc, kSampler2D_GrSLType, - false) // false because we don't upload MIP data in Vk yet + , INHERITED(gpu, desc, kSampler2D_GrSLType, desc.fIsMipMapped) , fTextureView(view) { this->registerWithCacheWrapped(); } @@ -50,8 +47,7 @@ GrVkTexture::GrVkTexture(GrVkGpu* gpu, const GrVkImageView* view) : GrSurface(gpu, desc) , GrVkImage(imageResource) - , INHERITED(gpu, desc, kSampler2D_GrSLType, - false) // false because we don't upload MIP data in Vk yet + , INHERITED(gpu, desc, kSampler2D_GrSLType, desc.fIsMipMapped) , fTextureView(view) {} @@ -59,17 +55,13 @@ template GrVkTexture* GrVkTexture::Create(GrVkGpu* gpu, ResourceType type, const GrSurfaceDesc& desc, - VkFormat format, + VkFormat format, uint32_t levels, const GrVkImage::Resource* imageResource) { VkImage image = imageResource->fImage; - uint32_t mipLevels = 1; - // TODO: enable when mipLevel loading is implemented in GrVkGpu - //if (desc.fIsMipMapped) { - // mipLevels = SkMipMap::ComputeLevelCount(this->width(), this->height()); - //} const GrVkImageView* imageView = GrVkImageView::Create(gpu, image, format, - GrVkImageView::kColor_Type, mipLevels); + GrVkImageView::kColor_Type, + levels); if (!imageView) { return nullptr; } @@ -87,7 +79,8 @@ GrVkTexture* GrVkTexture::CreateNewTexture(GrVkGpu* gpu, SkBudgeted budgeted, return nullptr; } - GrVkTexture* texture = Create(gpu, budgeted, desc, imageDesc.fFormat, imageResource); + GrVkTexture* texture = Create(gpu, budgeted, desc, imageDesc.fFormat, imageDesc.fLevels, + imageResource); // Create() will increment the refCount of the image resource if it succeeds imageResource->unref(gpu); @@ -119,7 +112,8 @@ GrVkTexture* GrVkTexture::CreateWrappedTexture(GrVkGpu* gpu, return nullptr; } - GrVkTexture* texture = Create(gpu, kWrapped, desc, format, imageResource); + // We have no other information so we have to assume that wrapped textures have only one level + GrVkTexture* texture = Create(gpu, kWrapped, desc, format, 1, imageResource); if (texture) { texture->fCurrentLayout = info->fImageLayout; } @@ -166,9 +160,19 @@ GrVkGpu* GrVkTexture::getVkGpu() const { return static_cast(this->getGpu()); } -bool GrVkTexture::reallocForMipmap(const GrVkGpu* gpu) { +bool GrVkTexture::reallocForMipmap(const GrVkGpu* gpu, uint32_t mipLevels) { + if (mipLevels == 1) { + // don't need to do anything for a 1x1 texture + return false; + } + const GrVkImage::Resource* oldResource = fResource; + // We shouldn't realloc something that doesn't belong to us + if (GrVkImage::Resource::kBorrowed_Flag & oldResource->fFlags) { + return false; + } + // Does this even make sense for rendertargets? bool renderTarget = SkToBool(fDesc.fFlags & kRenderTarget_GrSurfaceFlag); @@ -178,12 +182,6 @@ bool GrVkTexture::reallocForMipmap(const GrVkGpu* gpu) { } usageFlags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - uint32_t mipLevels = SkMipMap::ComputeLevelCount(this->width(), this->height()); - if (mipLevels == 1) { - // don't need to do anything for a 1x1 texture - return false; - } - GrVkImage::ImageDesc imageDesc; imageDesc.fImageType = VK_IMAGE_TYPE_2D; imageDesc.fFormat = oldResource->fFormat; diff --git a/src/gpu/vk/GrVkTexture.h b/src/gpu/vk/GrVkTexture.h index a5e82e866c..be52c17a5f 100644 --- a/src/gpu/vk/GrVkTexture.h +++ b/src/gpu/vk/GrVkTexture.h @@ -33,7 +33,7 @@ public: const GrVkImageView* textureView() const { return fTextureView; } - bool reallocForMipmap(const GrVkGpu* gpu); + bool reallocForMipmap(const GrVkGpu* gpu, uint32_t mipLevels); protected: GrVkTexture(GrVkGpu*, const GrSurfaceDesc&, @@ -41,7 +41,7 @@ protected: template static GrVkTexture* Create(GrVkGpu*, ResourceType, const GrSurfaceDesc&, VkFormat, - const GrVkImage::Resource* texImpl); + uint32_t levels, const GrVkImage::Resource* texImpl); GrVkGpu* getVkGpu() const; @@ -55,8 +55,7 @@ private: GrVkTexture(GrVkGpu*, Wrapped, const GrSurfaceDesc&, const GrVkImage::Resource*, const GrVkImageView* imageView); - - const GrVkImageView* fTextureView; + const GrVkImageView* fTextureView; typedef GrTexture INHERITED; }; -- cgit v1.2.3