Vulkan: Create TextureVk's Image lazily.
This defers the actual Image initialization until the Image is used
as either a Framebuffer Attachment or OpenGL Texture object. This
will allow us to construct an Image from multiple sub resources, like
when we're initializing a mip chain, or a cube map texture.
Also adds a helper "hasDepthOrStencilBits" function to angle::Format.
Bug: angleproject:2318
Change-Id: Ife861560216581a90fc6da32a583f69886c7daea
Reviewed-on: https://chromium-review.googlesource.com/985202
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.cpp b/src/libANGLE/renderer/vulkan/TextureVk.cpp
index 98a7055..70f277e 100644
--- a/src/libANGLE/renderer/vulkan/TextureVk.cpp
+++ b/src/libANGLE/renderer/vulkan/TextureVk.cpp
@@ -25,19 +25,19 @@
{
switch (internalFormat)
{
- case GL_LUMINANCE:
+ case GL_LUMINANCE8_OES:
swizzleStateOut->swizzleRed = swizzleState.swizzleRed;
swizzleStateOut->swizzleGreen = swizzleState.swizzleRed;
swizzleStateOut->swizzleBlue = swizzleState.swizzleRed;
swizzleStateOut->swizzleAlpha = GL_ONE;
break;
- case GL_LUMINANCE_ALPHA:
+ case GL_LUMINANCE8_ALPHA8_OES:
swizzleStateOut->swizzleRed = swizzleState.swizzleRed;
swizzleStateOut->swizzleGreen = swizzleState.swizzleRed;
swizzleStateOut->swizzleBlue = swizzleState.swizzleRed;
swizzleStateOut->swizzleAlpha = swizzleState.swizzleGreen;
break;
- case GL_ALPHA:
+ case GL_ALPHA8_OES:
swizzleStateOut->swizzleRed = GL_ZERO;
swizzleStateOut->swizzleGreen = GL_ZERO;
swizzleStateOut->swizzleBlue = GL_ZERO;
@@ -48,8 +48,110 @@
break;
}
}
+
+constexpr VkBufferUsageFlags kStagingBufferFlags =
+ (VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
+constexpr size_t kStagingBufferSize = 1024 * 16;
} // anonymous namespace
+// StagingStorage implementation.
+StagingStorage::StagingStorage()
+ : mStagingBuffer(kStagingBufferFlags, kStagingBufferSize), mCurrentBufferHandle(VK_NULL_HANDLE)
+{
+ mStagingBuffer.init(1);
+}
+
+StagingStorage::~StagingStorage()
+{
+}
+
+void StagingStorage::release(RendererVk *renderer)
+{
+ mStagingBuffer.release(renderer);
+}
+
+gl::Error StagingStorage::stageSubresourceUpdate(ContextVk *contextVk,
+ const gl::Extents &extents,
+ const gl::InternalFormat &formatInfo,
+ const gl::PixelUnpackState &unpack,
+ GLenum type,
+ const uint8_t *pixels)
+{
+ GLuint inputRowPitch = 0;
+ ANGLE_TRY_RESULT(
+ formatInfo.computeRowPitch(type, extents.width, unpack.alignment, unpack.rowLength),
+ inputRowPitch);
+
+ GLuint inputDepthPitch = 0;
+ ANGLE_TRY_RESULT(
+ formatInfo.computeDepthPitch(extents.height, unpack.imageHeight, inputRowPitch),
+ inputDepthPitch);
+
+ // TODO(jmadill): skip images for 3D Textures.
+ bool applySkipImages = false;
+
+ GLuint inputSkipBytes = 0;
+ ANGLE_TRY_RESULT(
+ formatInfo.computeSkipBytes(inputRowPitch, inputDepthPitch, unpack, applySkipImages),
+ inputSkipBytes);
+
+ RendererVk *renderer = contextVk->getRenderer();
+
+ const vk::Format &vkFormat = renderer->getFormat(formatInfo.sizedInternalFormat);
+ const angle::Format &storageFormat = vkFormat.textureFormat();
+
+ size_t outputRowPitch = storageFormat.pixelBytes * extents.width;
+ size_t outputDepthPitch = outputRowPitch * extents.height;
+
+ uint8_t *stagingPointer = nullptr;
+ bool newBufferAllocated = false;
+ uint32_t stagingOffset = 0;
+ size_t allocationSize = outputDepthPitch * extents.depth;
+ mStagingBuffer.allocate(renderer, allocationSize, &stagingPointer, &mCurrentBufferHandle,
+ &stagingOffset, &newBufferAllocated);
+
+ const uint8_t *source = pixels + inputSkipBytes;
+
+ LoadImageFunctionInfo loadFunction = vkFormat.loadFunctions(type);
+
+ loadFunction.loadFunction(extents.width, extents.height, extents.depth, source, inputRowPitch,
+ inputDepthPitch, stagingPointer, outputRowPitch, outputDepthPitch);
+
+ mCurrentCopyRegion.bufferOffset = static_cast<VkDeviceSize>(stagingOffset);
+ mCurrentCopyRegion.bufferRowLength = extents.width;
+ mCurrentCopyRegion.bufferImageHeight = extents.height;
+ mCurrentCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ mCurrentCopyRegion.imageSubresource.mipLevel = 0;
+ mCurrentCopyRegion.imageSubresource.baseArrayLayer = 0;
+ mCurrentCopyRegion.imageSubresource.layerCount = 1;
+
+ gl_vk::GetOffset(gl::Offset(), &mCurrentCopyRegion.imageOffset);
+ gl_vk::GetExtent(extents, &mCurrentCopyRegion.imageExtent);
+
+ return gl::NoError();
+}
+
+vk::Error StagingStorage::flushUpdatesToImage(RendererVk *renderer,
+ vk::ImageHelper *image,
+ vk::CommandBuffer *commandBuffer)
+{
+ if (mCurrentBufferHandle != VK_NULL_HANDLE)
+ {
+ // Conservatively flush all writes to the image. We could use a more restricted barrier.
+ image->changeLayoutWithStages(
+ VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer);
+
+ ANGLE_TRY(mStagingBuffer.flush(renderer->getDevice()));
+ commandBuffer->copyBufferToImage(mCurrentBufferHandle, image->getImage(),
+ image->getCurrentLayout(), 1, &mCurrentCopyRegion);
+ mCurrentBufferHandle = VK_NULL_HANDLE;
+ }
+
+ return vk::NoError();
+}
+
+// TextureVk implementation.
TextureVk::TextureVk(const gl::TextureState &state) : TextureImpl(state)
{
mRenderTarget.image = &mImage;
@@ -71,6 +173,8 @@
renderer->releaseResource(*this, &mImageView);
renderer->releaseResource(*this, &mSampler);
+ mStagingStorage.release(renderer);
+
onStateChange(context, angle::SubjectMessage::DEPENDENT_DIRTY_BITS);
return gl::NoError();
@@ -109,39 +213,16 @@
}
// Early-out on empty textures, don't create a zero-sized storage.
- if (size.width == 0 || size.height == 0 || size.depth == 0)
+ if (size.empty())
{
return gl::NoError();
}
- // TODO(jmadill): support other types of textures.
- ASSERT(target == gl::TextureTarget::_2D);
-
- // Convert internalFormat to sized internal format.
- const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat, type);
- const vk::Format &vkFormat = renderer->getFormat(formatInfo.sizedInternalFormat);
-
- if (!mImage.valid())
+ // TODO(jmadill): Cube map textures. http://anglebug.com/2318
+ if (target != gl::TextureTarget::_2D)
{
- VkImageUsageFlags usage =
- (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
- VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
- ANGLE_TRY(mImage.init2D(device, size, vkFormat, 1, usage));
-
- VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
- ANGLE_TRY(mImage.initMemory(device, renderer->getMemoryProperties(), flags));
-
- gl::SwizzleState mappedSwizzle;
- MapSwizzleState(formatInfo.internalFormat, mState.getSwizzleState(), &mappedSwizzle);
-
- ANGLE_TRY(
- mImage.initImageView(device, VK_IMAGE_ASPECT_COLOR_BIT, mappedSwizzle, &mImageView));
-
- // TODO(jmadill): Fold this into the RenderPass load/store ops. http://anglebug.com/2361
- vk::CommandBuffer *commandBuffer = nullptr;
- ANGLE_TRY(beginWriteResource(renderer, &commandBuffer));
- VkClearColorValue black = {{0}};
- mImage.clearColor(black, commandBuffer);
+ UNIMPLEMENTED();
+ return gl::InternalError();
}
if (!mSampler.valid())
@@ -171,10 +252,16 @@
ANGLE_TRY(mSampler.init(device, samplerInfo));
}
+ // Create a new graph node to store image initialization commands.
+ getNewWritingNode(renderer);
+
// Handle initial data.
if (pixels)
{
- ANGLE_TRY(setSubImageImpl(contextVk, formatInfo, unpack, type, pixels));
+ // Convert internalFormat to sized internal format.
+ const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat, type);
+ ANGLE_TRY(mStagingStorage.stageSubresourceUpdate(contextVk, size, formatInfo, unpack, type,
+ pixels));
}
return gl::NoError();
@@ -191,71 +278,9 @@
{
ContextVk *contextVk = vk::GetImpl(context);
const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, type);
- ANGLE_TRY(setSubImageImpl(contextVk, formatInfo, unpack, type, pixels));
- return gl::NoError();
-}
-
-gl::Error TextureVk::setSubImageImpl(ContextVk *contextVk,
- const gl::InternalFormat &formatInfo,
- const gl::PixelUnpackState &unpack,
- GLenum type,
- const uint8_t *pixels)
-{
- RendererVk *renderer = contextVk->getRenderer();
- VkDevice device = renderer->getDevice();
- const gl::Extents &size = mImage.getExtents();
- const vk::Format &vkFormat = mImage.getFormat();
-
- vk::ImageHelper stagingImage;
- ANGLE_TRY(stagingImage.init2DStaging(device, renderer->getMemoryProperties(), vkFormat, size,
- vk::StagingUsage::Write));
-
- GLuint inputRowPitch = 0;
- ANGLE_TRY_RESULT(
- formatInfo.computeRowPitch(type, size.width, unpack.alignment, unpack.rowLength),
- inputRowPitch);
-
- GLuint inputDepthPitch = 0;
- ANGLE_TRY_RESULT(formatInfo.computeDepthPitch(size.height, unpack.imageHeight, inputRowPitch),
- inputDepthPitch);
-
- // TODO(jmadill): skip images for 3D Textures.
- bool applySkipImages = false;
-
- GLuint inputSkipBytes = 0;
- ANGLE_TRY_RESULT(
- formatInfo.computeSkipBytes(inputRowPitch, inputDepthPitch, unpack, applySkipImages),
- inputSkipBytes);
-
- auto loadFunction = vkFormat.loadFunctions(type);
-
- uint8_t *mapPointer = nullptr;
- ANGLE_TRY(stagingImage.getDeviceMemory().map(device, 0, VK_WHOLE_SIZE, 0, &mapPointer));
-
- const uint8_t *source = pixels + inputSkipBytes;
-
- // Get the subresource layout. This has important parameters like row pitch.
- // TODO(jmadill): Fill out these parameters based on input parameters.
- VkSubresourceLayout subresourceLayout;
- stagingImage.getImage().getSubresourceLayout(device, VK_IMAGE_ASPECT_COLOR_BIT, 0, 0,
- &subresourceLayout);
-
- loadFunction.loadFunction(size.width, size.height, size.depth, source, inputRowPitch,
- inputDepthPitch, mapPointer,
- static_cast<size_t>(subresourceLayout.rowPitch),
- static_cast<size_t>(subresourceLayout.depthPitch));
-
- stagingImage.getDeviceMemory().unmap(device);
-
- vk::CommandBuffer *commandBuffer = nullptr;
- ANGLE_TRY(beginWriteResource(renderer, &commandBuffer));
-
- vk::ImageHelper::Copy(&stagingImage, &mImage, gl::Offset(), gl::Offset(), size,
- VK_IMAGE_ASPECT_COLOR_BIT, commandBuffer);
-
- // Immediately release staging image.
- // TODO(jmadill): Staging image re-use.
- renderer->releaseObject(renderer->getCurrentQueueSerial(), &stagingImage);
+ ANGLE_TRY(mStagingStorage.stageSubresourceUpdate(
+ contextVk, gl::Extents(area.width, area.height, area.depth), formatInfo, unpack, type,
+ pixels));
return gl::NoError();
}
@@ -363,12 +388,73 @@
const gl::ImageIndex &imageIndex,
FramebufferAttachmentRenderTarget **rtOut)
{
+ // TODO(jmadill): Handle cube textures. http://anglebug.com/2318
ASSERT(imageIndex.type == gl::TextureType::_2D);
+
+ // Non-zero mip level attachments are an ES 3.0 feature.
ASSERT(imageIndex.mipIndex == 0 && imageIndex.layerIndex == gl::ImageIndex::ENTIRE_LEVEL);
+
+ ContextVk *contextVk = vk::GetImpl(context);
+ RendererVk *renderer = contextVk->getRenderer();
+
+ ANGLE_TRY(ensureImageInitialized(renderer));
+
*rtOut = &mRenderTarget;
return gl::NoError();
}
+vk::Error TextureVk::ensureImageInitialized(RendererVk *renderer)
+{
+ VkDevice device = renderer->getDevice();
+ vk::CommandBuffer *commandBuffer = nullptr;
+
+ updateQueueSerial(renderer->getCurrentQueueSerial());
+ if (!hasChildlessWritingNode())
+ {
+ beginWriteResource(renderer, &commandBuffer);
+ }
+ else
+ {
+ vk::CommandGraphNode *node = getCurrentWritingNode();
+ commandBuffer = node->getOutsideRenderPassCommands();
+ if (!commandBuffer->valid())
+ {
+ ANGLE_TRY(node->beginOutsideRenderPassRecording(device, renderer->getCommandPool(),
+ &commandBuffer));
+ }
+ }
+
+ if (!mImage.valid())
+ {
+ const gl::ImageDesc &baseLevelDesc = mState.getBaseLevelDesc();
+ const gl::Extents &extents = baseLevelDesc.size;
+ const vk::Format &format =
+ renderer->getFormat(baseLevelDesc.format.info->sizedInternalFormat);
+
+ VkImageUsageFlags usage =
+ (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
+ ANGLE_TRY(mImage.init2D(device, extents, format, 1, usage));
+
+ VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+ ANGLE_TRY(mImage.initMemory(device, renderer->getMemoryProperties(), flags));
+
+ gl::SwizzleState mappedSwizzle;
+ MapSwizzleState(format.internalFormat, mState.getSwizzleState(), &mappedSwizzle);
+
+ ANGLE_TRY(
+ mImage.initImageView(device, VK_IMAGE_ASPECT_COLOR_BIT, mappedSwizzle, &mImageView));
+
+ // TODO(jmadill): Fold this into the RenderPass load/store ops. http://anglebug.com/2361
+
+ VkClearColorValue black = {{0}};
+ mImage.clearColor(black, commandBuffer);
+ }
+
+ ANGLE_TRY(mStagingStorage.flushUpdatesToImage(renderer, &mImage, commandBuffer));
+ return vk::NoError();
+}
+
void TextureVk::syncState(const gl::Texture::DirtyBits &dirtyBits)
{
// TODO(jmadill): Texture sync state.