aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar jvanverth <jvanverth@google.com>2016-04-29 13:53:12 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-04-29 13:53:12 -0700
commit900bd4a0463bc6471ef07a77120b413bd8f472b2 (patch)
tree55c83ffd9c4dc84dbcf7b3bba4f6f2de8008583c
parent2f124631583a838edaa1e48362031cc6c7f40764 (diff)
Add mipmap loading to Vulkan.
-rw-r--r--src/gpu/GrGpu.cpp19
-rw-r--r--src/gpu/GrGpu.h3
-rw-r--r--src/gpu/gl/GrGLGpu.cpp27
-rw-r--r--src/gpu/gl/GrGLGpu.h2
-rw-r--r--src/gpu/vk/GrVkGpu.cpp392
-rw-r--r--src/gpu/vk/GrVkGpu.h16
-rw-r--r--src/gpu/vk/GrVkImage.h21
-rw-r--r--src/gpu/vk/GrVkTexture.cpp44
-rw-r--r--src/gpu/vk/GrVkTexture.h7
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<GrPathRendering> 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<const char*>(data);
- char* dstRow = reinterpret_cast<char*>(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<const char*>(data);
+ char* dstRow = reinterpret_cast<char*>(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<size_t>(layout.rowPitch), data, rowBytes,
- trimRowBytes, height);
- }
+ SkRectMemcpy(mapPtr, static_cast<size_t>(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<const char*>(data);
- char* dstRow = reinterpret_cast<char*>(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<GrMipLevel>& 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<GrMipLevel>* texelsPtr = &texels;
+ SkTArray<GrMipLevel> 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<size_t> 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<VkBufferImageCopy> 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(&region, 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,
- &region);
+ 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,
&copyRegion);
+
+ 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<GrMipLevel>&);
SkAutoTUnref<const GrVkBackendContext> fBackendContext;
SkAutoTUnref<GrVkCaps> 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<typename ResourceType>
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<GrVkGpu*>(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<typename ResourceType>
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;
};