Commit 08f8fa66 by Geoff Lang Committed by Commit Bot

Vulkan: Merge the PixelBuffer logic into ImageHelper.

To support EGL images efficiently, the pixel buffer needs to be tightly coupled with the image it will upload data to allowing textures to upload data to EGL images sourced from renderbuffers. The data will be uploaded to the image in the correct order if it comes from multiple sources and the check for pending uploads does not need to traverse EGL image siblings. BUG=angleproject:2668 Change-Id: Iebf59ed1e358ddc76843b8bcfac39b0363f66a3f Reviewed-on: https://chromium-review.googlesource.com/c/1405710 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org>
parent 9544002e
......@@ -34,7 +34,9 @@ void RenderbufferVk::onDestroy(const gl::Context *context)
ContextVk *contextVk = vk::GetImpl(context);
RendererVk *renderer = contextVk->getRenderer();
mImage.release(renderer);
mImage.releaseImage(renderer);
mImage.releaseStagingBuffer(renderer);
renderer->releaseObject(renderer->getCurrentQueueSerial(), &mImageView);
}
......@@ -54,7 +56,7 @@ angle::Result RenderbufferVk::setStorage(const gl::Context *context,
static_cast<GLsizei>(width) != mState.getWidth() ||
static_cast<GLsizei>(height) != mState.getHeight())
{
mImage.release(renderer);
mImage.releaseImage(renderer);
renderer->releaseObject(renderer->getCurrentQueueSerial(), &mImageView);
}
}
......
......@@ -111,7 +111,8 @@ void OffscreenSurfaceVk::AttachmentImage::destroy(const egl::Display *display)
const DisplayVk *displayVk = vk::GetImpl(display);
RendererVk *renderer = displayVk->getRenderer();
image.release(renderer);
image.releaseImage(renderer);
image.releaseStagingBuffer(renderer);
renderer->releaseObject(renderer->getCurrentQueueSerial(), &imageView);
}
......@@ -294,7 +295,8 @@ void WindowSurfaceVk::destroy(const egl::Display *display)
// We might not need to flush the pipe here.
(void)renderer->finish(displayVk);
mDepthStencilImage.release(renderer);
mDepthStencilImage.releaseImage(renderer);
mDepthStencilImage.releaseStagingBuffer(renderer);
mDepthStencilImageView.destroy(device);
for (SwapchainImage &swapchainImage : mSwapchainImages)
......
......@@ -21,10 +21,6 @@ namespace rx
{
namespace
{
constexpr VkBufferUsageFlags kStagingBufferFlags =
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
constexpr size_t kStagingBufferSize = 1024 * 16;
constexpr VkImageUsageFlags kStagingImageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
......@@ -73,359 +69,6 @@ gl::TextureType Get2DTextureType(uint32_t layerCount, GLint samples)
}
} // anonymous namespace
// StagingStorage implementation.
PixelBuffer::PixelBuffer(RendererVk *renderer)
: mStagingBuffer(kStagingBufferFlags, kStagingBufferSize, true)
{
// vkCmdCopyBufferToImage must have an offset that is a multiple of 4.
// https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkBufferImageCopy.html
mStagingBuffer.init(4, renderer);
}
PixelBuffer::~PixelBuffer() {}
void PixelBuffer::release(RendererVk *renderer)
{
// Remove updates that never made it to the texture.
for (SubresourceUpdate &update : mSubresourceUpdates)
{
update.release(renderer);
}
mStagingBuffer.release(renderer);
mSubresourceUpdates.clear();
}
void PixelBuffer::removeStagedUpdates(RendererVk *renderer, const gl::ImageIndex &index)
{
// Find any staged updates for this index and removes them from the pending list.
uint32_t levelIndex = index.getLevelIndex();
uint32_t layerIndex = index.hasLayer() ? index.getLayerIndex() : 0;
for (size_t index = 0; index < mSubresourceUpdates.size();)
{
auto update = mSubresourceUpdates.begin() + index;
if (update->isUpdateToLayerLevel(layerIndex, levelIndex))
{
update->release(renderer);
mSubresourceUpdates.erase(update);
}
else
{
index++;
}
}
}
angle::Result PixelBuffer::stageSubresourceUpdate(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Extents &extents,
const gl::Offset &offset,
const gl::InternalFormat &formatInfo,
const gl::PixelUnpackState &unpack,
GLenum type,
const uint8_t *pixels)
{
GLuint inputRowPitch = 0;
ANGLE_VK_CHECK_MATH(contextVk, formatInfo.computeRowPitch(type, extents.width, unpack.alignment,
unpack.rowLength, &inputRowPitch));
GLuint inputDepthPitch = 0;
ANGLE_VK_CHECK_MATH(contextVk, formatInfo.computeDepthPitch(extents.height, unpack.imageHeight,
inputRowPitch, &inputDepthPitch));
// TODO(jmadill): skip images for 3D Textures.
bool applySkipImages = false;
GLuint inputSkipBytes = 0;
ANGLE_VK_CHECK_MATH(contextVk,
formatInfo.computeSkipBytes(type, 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;
VkBuffer bufferHandle = VK_NULL_HANDLE;
uint8_t *stagingPointer = nullptr;
VkDeviceSize stagingOffset = 0;
size_t allocationSize = outputDepthPitch * extents.depth;
ANGLE_TRY(mStagingBuffer.allocate(contextVk, allocationSize, &stagingPointer, &bufferHandle,
&stagingOffset, nullptr));
const uint8_t *source = pixels + inputSkipBytes;
LoadImageFunctionInfo loadFunction = vkFormat.textureLoadFunctions(type);
loadFunction.loadFunction(extents.width, extents.height, extents.depth, source, inputRowPitch,
inputDepthPitch, stagingPointer, outputRowPitch, outputDepthPitch);
VkBufferImageCopy copy = {};
copy.bufferOffset = stagingOffset;
copy.bufferRowLength = extents.width;
copy.bufferImageHeight = extents.height;
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy.imageSubresource.mipLevel = index.getLevelIndex();
copy.imageSubresource.baseArrayLayer = index.hasLayer() ? index.getLayerIndex() : 0;
copy.imageSubresource.layerCount = index.getLayerCount();
gl_vk::GetOffset(offset, &copy.imageOffset);
gl_vk::GetExtent(extents, &copy.imageExtent);
mSubresourceUpdates.emplace_back(bufferHandle, copy);
return angle::Result::Continue;
}
angle::Result PixelBuffer::stageSubresourceUpdateFromFramebuffer(
const gl::Context *context,
const gl::ImageIndex &index,
const gl::Rectangle &sourceArea,
const gl::Offset &dstOffset,
const gl::Extents &dstExtent,
const gl::InternalFormat &formatInfo,
FramebufferVk *framebufferVk)
{
ContextVk *contextVk = vk::GetImpl(context);
// If the extents and offset is outside the source image, we need to clip.
gl::Rectangle clippedRectangle;
const gl::Extents readExtents = framebufferVk->getReadImageExtents();
if (!ClipRectangle(sourceArea, gl::Rectangle(0, 0, readExtents.width, readExtents.height),
&clippedRectangle))
{
// Empty source area, nothing to do.
return angle::Result::Continue;
}
bool isViewportFlipEnabled = contextVk->isViewportFlipEnabledForDrawFBO();
if (isViewportFlipEnabled)
{
clippedRectangle.y = readExtents.height - clippedRectangle.y - clippedRectangle.height;
}
// 1- obtain a buffer handle to copy to
RendererVk *renderer = contextVk->getRenderer();
const vk::Format &vkFormat = renderer->getFormat(formatInfo.sizedInternalFormat);
const angle::Format &storageFormat = vkFormat.textureFormat();
LoadImageFunctionInfo loadFunction = vkFormat.textureLoadFunctions(formatInfo.type);
size_t outputRowPitch = storageFormat.pixelBytes * clippedRectangle.width;
size_t outputDepthPitch = outputRowPitch * clippedRectangle.height;
VkBuffer bufferHandle = VK_NULL_HANDLE;
uint8_t *stagingPointer = nullptr;
VkDeviceSize stagingOffset = 0;
// The destination is only one layer deep.
size_t allocationSize = outputDepthPitch;
ANGLE_TRY(mStagingBuffer.allocate(contextVk, allocationSize, &stagingPointer, &bufferHandle,
&stagingOffset, nullptr));
const angle::Format &copyFormat =
GetFormatFromFormatType(formatInfo.internalFormat, formatInfo.type);
PackPixelsParams params(clippedRectangle, copyFormat, static_cast<GLuint>(outputRowPitch),
isViewportFlipEnabled, nullptr, 0);
// 2- copy the source image region to the pixel buffer using a cpu readback
if (loadFunction.requiresConversion)
{
// When a conversion is required, we need to use the loadFunction to read from a temporary
// buffer instead so its an even slower path.
size_t bufferSize =
storageFormat.pixelBytes * clippedRectangle.width * clippedRectangle.height;
angle::MemoryBuffer *memoryBuffer = nullptr;
ANGLE_VK_CHECK_ALLOC(contextVk, context->getScratchBuffer(bufferSize, &memoryBuffer));
// Read into the scratch buffer
ANGLE_TRY(framebufferVk->readPixelsImpl(
contextVk, clippedRectangle, params, VK_IMAGE_ASPECT_COLOR_BIT,
framebufferVk->getColorReadRenderTarget(), memoryBuffer->data()));
// Load from scratch buffer to our pixel buffer
loadFunction.loadFunction(clippedRectangle.width, clippedRectangle.height, 1,
memoryBuffer->data(), outputRowPitch, 0, stagingPointer,
outputRowPitch, 0);
}
else
{
// We read directly from the framebuffer into our pixel buffer.
ANGLE_TRY(framebufferVk->readPixelsImpl(
contextVk, clippedRectangle, params, VK_IMAGE_ASPECT_COLOR_BIT,
framebufferVk->getColorReadRenderTarget(), stagingPointer));
}
// 3- enqueue the destination image subresource update
VkBufferImageCopy copyToImage = {};
copyToImage.bufferOffset = static_cast<VkDeviceSize>(stagingOffset);
copyToImage.bufferRowLength = 0; // Tightly packed data can be specified as 0.
copyToImage.bufferImageHeight = clippedRectangle.height;
copyToImage.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyToImage.imageSubresource.mipLevel = index.getLevelIndex();
copyToImage.imageSubresource.baseArrayLayer = index.hasLayer() ? index.getLayerIndex() : 0;
copyToImage.imageSubresource.layerCount = index.getLayerCount();
gl_vk::GetOffset(dstOffset, &copyToImage.imageOffset);
gl_vk::GetExtent(dstExtent, &copyToImage.imageExtent);
// 3- enqueue the destination image subresource update
mSubresourceUpdates.emplace_back(bufferHandle, copyToImage);
return angle::Result::Continue;
}
void PixelBuffer::stageSubresourceUpdateFromImage(vk::ImageHelper *image,
const gl::ImageIndex &index,
const gl::Offset &destOffset,
const gl::Extents &extents)
{
VkImageCopy copyToImage = {};
copyToImage.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyToImage.srcSubresource.layerCount = index.getLayerCount();
copyToImage.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyToImage.dstSubresource.mipLevel = index.getLevelIndex();
copyToImage.dstSubresource.baseArrayLayer = index.hasLayer() ? index.getLayerIndex() : 0;
copyToImage.dstSubresource.layerCount = index.getLayerCount();
gl_vk::GetOffset(destOffset, &copyToImage.dstOffset);
gl_vk::GetExtent(extents, &copyToImage.extent);
mSubresourceUpdates.emplace_back(image, copyToImage);
}
angle::Result PixelBuffer::allocate(ContextVk *contextVk,
size_t sizeInBytes,
uint8_t **ptrOut,
VkBuffer *handleOut,
VkDeviceSize *offsetOut,
bool *newBufferAllocatedOut)
{
return mStagingBuffer.allocate(contextVk, sizeInBytes, ptrOut, handleOut, offsetOut,
newBufferAllocatedOut);
}
angle::Result PixelBuffer::flushUpdatesToImage(ContextVk *contextVk,
uint32_t levelCount,
vk::ImageHelper *image,
vk::CommandBuffer *commandBuffer)
{
if (mSubresourceUpdates.empty())
{
return angle::Result::Continue;
}
RendererVk *renderer = contextVk->getRenderer();
ANGLE_TRY(mStagingBuffer.flush(contextVk));
std::vector<SubresourceUpdate> updatesToKeep;
for (SubresourceUpdate &update : mSubresourceUpdates)
{
ASSERT((update.updateSource == SubresourceUpdate::UpdateSource::Buffer &&
update.buffer.bufferHandle != VK_NULL_HANDLE) ||
(update.updateSource == SubresourceUpdate::UpdateSource::Image &&
update.image.image != nullptr && update.image.image->valid()));
const uint32_t updateMipLevel = update.dstSubresource().mipLevel;
// It's possible we've accumulated updates that are no longer applicable if the image has
// never been flushed but the image description has changed. Check if this level exist for
// this image.
if (updateMipLevel >= levelCount)
{
updatesToKeep.emplace_back(update);
continue;
}
// Conservatively flush all writes to the image. We could use a more restricted barrier.
// Do not move this above the for loop, otherwise multiple updates can have race conditions
// and not be applied correctly as seen in:
// dEQP-gles2.functional_texture_specification_texsubimage2d_align_2d* tests on Windows AMD
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);
if (update.updateSource == SubresourceUpdate::UpdateSource::Buffer)
{
commandBuffer->copyBufferToImage(update.buffer.bufferHandle, image->getImage(),
image->getCurrentLayout(), 1,
&update.buffer.copyRegion);
}
else
{
// Note: currently, the staging images are only made through color attachment writes. If
// they were written to otherwise in the future, the src stage of this transition should
// be adjusted appropriately.
update.image.image->changeLayoutWithStages(
VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
commandBuffer);
update.image.image->addReadDependency(image);
commandBuffer->copyImage(update.image.image->getImage(),
update.image.image->getCurrentLayout(), image->getImage(),
image->getCurrentLayout(), 1, &update.image.copyRegion);
}
update.release(renderer);
}
// Only remove the updates that were actually applied to the image.
mSubresourceUpdates = std::move(updatesToKeep);
if (mSubresourceUpdates.empty())
{
mStagingBuffer.releaseRetainedBuffers(contextVk->getRenderer());
}
else
{
WARN() << "Internal Vulkan buffer could not be released. This is likely due to having "
"extra images defined in the Texture.";
}
return angle::Result::Continue;
}
bool PixelBuffer::empty() const
{
return mSubresourceUpdates.empty();
}
angle::Result PixelBuffer::stageSubresourceUpdateAndGetData(ContextVk *contextVk,
size_t allocationSize,
const gl::ImageIndex &imageIndex,
const gl::Extents &extents,
const gl::Offset &offset,
uint8_t **destData)
{
VkBuffer bufferHandle;
VkDeviceSize stagingOffset = 0;
ANGLE_TRY(mStagingBuffer.allocate(contextVk, allocationSize, destData, &bufferHandle,
&stagingOffset, nullptr));
VkBufferImageCopy copy = {};
copy.bufferOffset = stagingOffset;
copy.bufferRowLength = extents.width;
copy.bufferImageHeight = extents.height;
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy.imageSubresource.mipLevel = imageIndex.getLevelIndex();
copy.imageSubresource.baseArrayLayer = imageIndex.hasLayer() ? imageIndex.getLayerIndex() : 0;
copy.imageSubresource.layerCount = imageIndex.getLayerCount();
gl_vk::GetOffset(offset, &copy.imageOffset);
gl_vk::GetExtent(extents, &copy.imageExtent);
mSubresourceUpdates.emplace_back(bufferHandle, copy);
return angle::Result::Continue;
}
angle::Result TextureVk::generateMipmapLevelsWithCPU(ContextVk *contextVk,
const angle::Format &sourceFormat,
GLuint layer,
......@@ -454,7 +97,7 @@ angle::Result TextureVk::generateMipmapLevelsWithCPU(ContextVk *contextVk,
size_t mipAllocationSize = destRowPitch * mipHeight;
gl::Extents mipLevelExtents(static_cast<int>(mipWidth), static_cast<int>(mipHeight), 1);
ANGLE_TRY(mPixelBuffer.stageSubresourceUpdateAndGetData(
ANGLE_TRY(mImage.stageSubresourceUpdateAndGetData(
contextVk, mipAllocationSize,
gl::ImageIndex::MakeFromType(mState.getType(), currentMipLevel, layer), mipLevelExtents,
gl::Offset(), &destData));
......@@ -474,55 +117,12 @@ angle::Result TextureVk::generateMipmapLevelsWithCPU(ContextVk *contextVk,
return angle::Result::Continue;
}
PixelBuffer::SubresourceUpdate::SubresourceUpdate()
: updateSource(UpdateSource::Buffer), buffer{VK_NULL_HANDLE}
{}
PixelBuffer::SubresourceUpdate::SubresourceUpdate(VkBuffer bufferHandleIn,
const VkBufferImageCopy &copyRegionIn)
: updateSource(UpdateSource::Buffer), buffer{bufferHandleIn, copyRegionIn}
{}
PixelBuffer::SubresourceUpdate::SubresourceUpdate(vk::ImageHelper *imageIn,
const VkImageCopy &copyRegionIn)
: updateSource(UpdateSource::Image), image{imageIn, copyRegionIn}
{}
PixelBuffer::SubresourceUpdate::SubresourceUpdate(const SubresourceUpdate &other)
: updateSource(other.updateSource)
{
if (updateSource == UpdateSource::Buffer)
{
buffer = other.buffer;
}
else
{
image = other.image;
}
}
void PixelBuffer::SubresourceUpdate::release(RendererVk *renderer)
{
if (updateSource == UpdateSource::Image)
{
image.image->release(renderer);
SafeDelete(image.image);
}
}
bool PixelBuffer::SubresourceUpdate::isUpdateToLayerLevel(uint32_t layerIndex,
uint32_t levelIndex) const
{
const VkImageSubresourceLayers &dst = dstSubresource();
return dst.baseArrayLayer == layerIndex && dst.mipLevel == levelIndex;
}
// TextureVk implementation.
TextureVk::TextureVk(const gl::TextureState &state, RendererVk *renderer)
: TextureImpl(state),
mRenderTarget(&mImage, &mDrawBaseLevelImageView, 0, this),
mPixelBuffer(renderer)
{}
: TextureImpl(state), mRenderTarget(&mImage, &mDrawBaseLevelImageView, 0, this)
{
mImage.initStagingBuffer(renderer);
}
TextureVk::~TextureVk() = default;
......@@ -531,10 +131,9 @@ void TextureVk::onDestroy(const gl::Context *context)
ContextVk *contextVk = vk::GetImpl(context);
RendererVk *renderer = contextVk->getRenderer();
releaseImage(context, renderer);
releaseImage(renderer);
releaseStagingBuffer(renderer);
renderer->releaseObject(renderer->getCurrentQueueSerial(), &mSampler);
mPixelBuffer.release(renderer);
}
angle::Result TextureVk::setImage(const gl::Context *context,
......@@ -566,8 +165,8 @@ angle::Result TextureVk::setImage(const gl::Context *context,
// Handle initial data.
if (pixels)
{
ANGLE_TRY(mPixelBuffer.stageSubresourceUpdate(contextVk, index, size, gl::Offset(),
formatInfo, unpack, type, pixels));
ANGLE_TRY(mImage.stageSubresourceUpdate(contextVk, index, size, gl::Offset(), formatInfo,
unpack, type, pixels));
}
return angle::Result::Continue;
......@@ -584,7 +183,7 @@ angle::Result TextureVk::setSubImage(const gl::Context *context,
{
ContextVk *contextVk = vk::GetImpl(context);
const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, type);
ANGLE_TRY(mPixelBuffer.stageSubresourceUpdate(
ANGLE_TRY(mImage.stageSubresourceUpdate(
contextVk, index, gl::Extents(area.width, area.height, area.depth),
gl::Offset(area.x, area.y, area.z), formatInfo, unpack, type, pixels));
......@@ -730,7 +329,7 @@ angle::Result TextureVk::copySubImageImpl(const gl::Context *context,
}
// Do a CPU readback that does the conversion, and then stage the change to the pixel buffer.
ANGLE_TRY(mPixelBuffer.stageSubresourceUpdateFromFramebuffer(
ANGLE_TRY(mImage.stageSubresourceUpdateFromFramebuffer(
context, index, clippedSourceArea, modifiedDestOffset,
gl::Extents(clippedSourceArea.width, clippedSourceArea.height, 1), internalFormat,
framebufferVk));
......@@ -788,7 +387,7 @@ angle::Result TextureVk::copySubTextureImpl(ContextVk *contextVk,
// Allocate memory in the destination texture for the copy/conversion
uint8_t *destData = nullptr;
ANGLE_TRY(mPixelBuffer.stageSubresourceUpdateAndGetData(
ANGLE_TRY(mImage.stageSubresourceUpdateAndGetData(
contextVk, destinationAllocationSize, index,
gl::Extents(sourceArea.width, sourceArea.height, 1), destOffset, &destData));
......@@ -911,9 +510,8 @@ angle::Result TextureVk::copySubImageImplWithDraw(ContextVk *contextVk,
}
// Stage the copy for when the image storage is actually created.
mPixelBuffer.stageSubresourceUpdateFromImage(
stagingImage.release(), index, destOffset,
gl::Extents(sourceArea.width, sourceArea.height, 1));
mImage.stageSubresourceUpdateFromImage(stagingImage.release(), index, destOffset,
gl::Extents(sourceArea.width, sourceArea.height, 1));
}
return angle::Result::Continue;
......@@ -933,7 +531,7 @@ angle::Result TextureVk::setStorage(const gl::Context *context,
if (mImage.valid())
{
releaseImage(context, renderer);
releaseImage(renderer);
}
ANGLE_TRY(initImage(contextVk, format, size, static_cast<uint32_t>(levels), commandBuffer));
......@@ -967,7 +565,7 @@ angle::Result TextureVk::redefineImage(const gl::Context *context,
// If there is any staged changes for this index, we can remove them since we're going to
// override them with this call.
mPixelBuffer.removeStagedUpdates(renderer, index);
mImage.removeStagedUpdates(renderer, index);
if (mImage.valid())
{
......@@ -978,7 +576,7 @@ angle::Result TextureVk::redefineImage(const gl::Context *context,
// release it.
if (mImage.getFormat() != vkFormat || size != mImage.getSize(index))
{
releaseImage(context, renderer);
releaseImage(renderer);
}
}
......@@ -1010,8 +608,8 @@ angle::Result TextureVk::copyImageDataToBuffer(ContextVk *contextVk,
// buffer.
VkBuffer copyBufferHandle = VK_NULL_HANDLE;
VkDeviceSize sourceCopyOffset = 0;
ANGLE_TRY(mPixelBuffer.allocate(contextVk, sourceCopyAllocationSize, outDataPtr,
&copyBufferHandle, &sourceCopyOffset, nullptr));
ANGLE_TRY(mImage.allocateStagingMemory(contextVk, sourceCopyAllocationSize, outDataPtr,
&copyBufferHandle, &sourceCopyOffset, nullptr));
VkBufferImageCopy region = {};
region.bufferOffset = sourceCopyOffset;
......@@ -1068,7 +666,7 @@ angle::Result TextureVk::generateMipmapsWithCPU(const gl::Context *context)
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(mImage.recordCommands(contextVk, &commandBuffer));
return mPixelBuffer.flushUpdatesToImage(contextVk, getLevelCount(), &mImage, commandBuffer);
return mImage.flushStagedUpdates(contextVk, getLevelCount(), commandBuffer);
}
angle::Result TextureVk::generateMipmap(const gl::Context *context)
......@@ -1079,7 +677,7 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context)
if (!mImage.valid())
{
// lets initialize the image so we can generate the next levels.
if (!mPixelBuffer.empty())
if (mImage.hasStagedUpdates())
{
ANGLE_TRY(ensureImageInitialized(contextVk));
ASSERT(mImage.valid());
......@@ -1174,7 +772,7 @@ angle::Result TextureVk::ensureImageInitializedImpl(ContextVk *contextVk,
uint32_t levelCount,
const vk::Format &format)
{
if (mImage.valid() && mPixelBuffer.empty())
if (mImage.valid() && !mImage.hasStagedUpdates())
{
return angle::Result::Continue;
}
......@@ -1186,7 +784,7 @@ angle::Result TextureVk::ensureImageInitializedImpl(ContextVk *contextVk,
ANGLE_TRY(initImage(contextVk, format, baseLevelExtents, levelCount, commandBuffer));
}
return mPixelBuffer.flushUpdatesToImage(contextVk, levelCount, &mImage, commandBuffer);
return mImage.flushStagedUpdates(contextVk, levelCount, commandBuffer);
}
angle::Result TextureVk::initCubeMapRenderTargets(ContextVk *contextVk)
......@@ -1354,9 +952,9 @@ angle::Result TextureVk::initImage(ContextVk *contextVk,
return angle::Result::Continue;
}
void TextureVk::releaseImage(const gl::Context *context, RendererVk *renderer)
void TextureVk::releaseImage(RendererVk *renderer)
{
mImage.release(renderer);
mImage.releaseImage(renderer);
Serial currentSerial = renderer->getCurrentQueueSerial();
......@@ -1378,6 +976,11 @@ void TextureVk::releaseImage(const gl::Context *context, RendererVk *renderer)
mCubeMapRenderTargets.clear();
}
void TextureVk::releaseStagingBuffer(RendererVk *renderer)
{
mImage.releaseStagingBuffer(renderer);
}
uint32_t TextureVk::getLevelCount() const
{
ASSERT(mState.getEffectiveBaseLevel() == 0);
......
......@@ -18,106 +18,6 @@
namespace rx
{
class PixelBuffer final : angle::NonCopyable
{
public:
PixelBuffer(RendererVk *renderer);
~PixelBuffer();
void release(RendererVk *renderer);
void removeStagedUpdates(RendererVk *renderer, const gl::ImageIndex &index);
angle::Result stageSubresourceUpdate(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Extents &extents,
const gl::Offset &offset,
const gl::InternalFormat &formatInfo,
const gl::PixelUnpackState &unpack,
GLenum type,
const uint8_t *pixels);
angle::Result stageSubresourceUpdateAndGetData(ContextVk *contextVk,
size_t allocationSize,
const gl::ImageIndex &imageIndex,
const gl::Extents &extents,
const gl::Offset &offset,
uint8_t **destData);
angle::Result stageSubresourceUpdateFromFramebuffer(const gl::Context *context,
const gl::ImageIndex &index,
const gl::Rectangle &sourceArea,
const gl::Offset &dstOffset,
const gl::Extents &dstExtent,
const gl::InternalFormat &formatInfo,
FramebufferVk *framebufferVk);
void stageSubresourceUpdateFromImage(vk::ImageHelper *image,
const gl::ImageIndex &index,
const gl::Offset &destOffset,
const gl::Extents &extents);
// This will use the underlying dynamic buffer to allocate some memory to be used as a src or
// dst.
angle::Result allocate(ContextVk *contextVk,
size_t sizeInBytes,
uint8_t **ptrOut,
VkBuffer *handleOut,
VkDeviceSize *offsetOut,
bool *newBufferAllocatedOut);
angle::Result flushUpdatesToImage(ContextVk *contextVk,
uint32_t levelCount,
vk::ImageHelper *image,
vk::CommandBuffer *commandBuffer);
bool empty() const;
private:
struct SubresourceUpdate
{
SubresourceUpdate();
SubresourceUpdate(VkBuffer bufferHandle, const VkBufferImageCopy &copyRegion);
SubresourceUpdate(vk::ImageHelper *image, const VkImageCopy &copyRegion);
SubresourceUpdate(const SubresourceUpdate &other);
void release(RendererVk *renderer);
const VkImageSubresourceLayers &dstSubresource() const
{
return updateSource == UpdateSource::Buffer ? buffer.copyRegion.imageSubresource
: image.copyRegion.dstSubresource;
}
bool isUpdateToLayerLevel(uint32_t layerIndex, uint32_t levelIndex) const;
enum class UpdateSource
{
Buffer,
Image,
};
struct BufferUpdate
{
VkBuffer bufferHandle;
VkBufferImageCopy copyRegion;
};
struct ImageUpdate
{
vk::ImageHelper *image;
VkImageCopy copyRegion;
};
UpdateSource updateSource;
union
{
BufferUpdate buffer;
ImageUpdate image;
};
};
vk::DynamicBuffer mStagingBuffer;
std::vector<SubresourceUpdate> mSubresourceUpdates;
};
class TextureVk : public TextureImpl
{
public:
......@@ -308,7 +208,8 @@ class TextureVk : public TextureImpl
const gl::Extents &extents,
const uint32_t levelCount,
vk::CommandBuffer *commandBuffer);
void releaseImage(const gl::Context *context, RendererVk *renderer);
void releaseImage(RendererVk *renderer);
void releaseStagingBuffer(RendererVk *renderer);
uint32_t getLevelCount() const;
angle::Result initCubeMapRenderTargets(ContextVk *contextVk);
......@@ -326,8 +227,6 @@ class TextureVk : public TextureImpl
RenderTargetVk mRenderTarget;
std::vector<RenderTargetVk> mCubeMapRenderTargets;
PixelBuffer mPixelBuffer;
};
} // namespace rx
......
......@@ -9,8 +9,10 @@
#include "libANGLE/renderer/vulkan/vk_helpers.h"
#include "common/utilities.h"
#include "libANGLE/Context.h"
#include "libANGLE/renderer/vulkan/BufferVk.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/FramebufferVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/vk_utils.h"
......@@ -122,6 +124,20 @@ DynamicBuffer::DynamicBuffer(VkBufferUsageFlags usage, size_t minSize, bool host
mAlignment(0)
{}
DynamicBuffer::DynamicBuffer(DynamicBuffer &&other)
: mUsage(other.mUsage),
mHostVisible(other.mHostVisible),
mMinSize(other.mMinSize),
mBuffer(other.mBuffer),
mNextAllocationOffset(other.mNextAllocationOffset),
mLastFlushOrInvalidateOffset(other.mLastFlushOrInvalidateOffset),
mSize(other.mSize),
mAlignment(other.mAlignment),
mRetainedBuffers(std::move(other.mRetainedBuffers))
{
other.mBuffer = nullptr;
}
void DynamicBuffer::init(size_t alignment, RendererVk *renderer)
{
// Workaround for the mock ICD not supporting allocations greater than 0x1000.
......@@ -137,7 +153,10 @@ void DynamicBuffer::init(size_t alignment, RendererVk *renderer)
static_cast<size_t>(renderer->getPhysicalDeviceProperties().limits.nonCoherentAtomSize));
}
DynamicBuffer::~DynamicBuffer() {}
DynamicBuffer::~DynamicBuffer()
{
ASSERT(mBuffer == nullptr);
}
angle::Result DynamicBuffer::allocate(Context *context,
size_t sizeInBytes,
......@@ -1092,6 +1111,14 @@ angle::Result BufferHelper::invalidate(Context *context, size_t offset, size_t s
return angle::Result::Continue;
}
namespace
{
constexpr VkBufferUsageFlags kStagingBufferFlags =
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
constexpr size_t kStagingBufferSize = 1024 * 16;
} // anonymous namespace
// ImageHelper implementation.
ImageHelper::ImageHelper()
: CommandGraphResource(CommandGraphResourceType::Image),
......@@ -1099,7 +1126,8 @@ ImageHelper::ImageHelper()
mSamples(0),
mCurrentLayout(VK_IMAGE_LAYOUT_UNDEFINED),
mLayerCount(0),
mLevelCount(0)
mLevelCount(0),
mStagingBuffer(kStagingBufferFlags, kStagingBufferSize, true)
{}
ImageHelper::ImageHelper(ImageHelper &&other)
......@@ -1111,7 +1139,9 @@ ImageHelper::ImageHelper(ImageHelper &&other)
mSamples(other.mSamples),
mCurrentLayout(other.mCurrentLayout),
mLayerCount(other.mLayerCount),
mLevelCount(other.mLevelCount)
mLevelCount(other.mLevelCount),
mStagingBuffer(std::move(other.mStagingBuffer)),
mSubresourceUpdates(std::move(other.mSubresourceUpdates))
{
other.mCurrentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
other.mLayerCount = 0;
......@@ -1123,6 +1153,13 @@ ImageHelper::~ImageHelper()
ASSERT(!valid());
}
void ImageHelper::initStagingBuffer(RendererVk *renderer)
{
// vkCmdCopyBufferToImage must have an offset that is a multiple of 4.
// https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkBufferImageCopy.html
mStagingBuffer.init(4, renderer);
}
angle::Result ImageHelper::init(Context *context,
gl::TextureType textureType,
const gl::Extents &extents,
......@@ -1167,15 +1204,27 @@ angle::Result ImageHelper::init(Context *context,
mCurrentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ANGLE_VK_TRY(context, mImage.init(context->getDevice(), imageInfo));
return angle::Result::Continue;
}
void ImageHelper::release(RendererVk *renderer)
void ImageHelper::releaseImage(RendererVk *renderer)
{
renderer->releaseObject(getStoredQueueSerial(), &mImage);
renderer->releaseObject(getStoredQueueSerial(), &mDeviceMemory);
}
void ImageHelper::releaseStagingBuffer(RendererVk *renderer)
{
// Remove updates that never made it to the texture.
for (SubresourceUpdate &update : mSubresourceUpdates)
{
update.release(renderer);
}
mStagingBuffer.release(renderer);
mSubresourceUpdates.clear();
}
void ImageHelper::resetImageWeakReference()
{
mImage.reset();
......@@ -1574,6 +1623,381 @@ angle::Result ImageHelper::generateMipmapsWithBlit(ContextVk *contextVk, GLuint
return angle::Result::Continue;
}
void ImageHelper::removeStagedUpdates(RendererVk *renderer, const gl::ImageIndex &index)
{
// Find any staged updates for this index and removes them from the pending list.
uint32_t levelIndex = index.getLevelIndex();
uint32_t layerIndex = index.hasLayer() ? index.getLayerIndex() : 0;
for (size_t index = 0; index < mSubresourceUpdates.size();)
{
auto update = mSubresourceUpdates.begin() + index;
if (update->isUpdateToLayerLevel(layerIndex, levelIndex))
{
update->release(renderer);
mSubresourceUpdates.erase(update);
}
else
{
index++;
}
}
}
angle::Result ImageHelper::stageSubresourceUpdate(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Extents &extents,
const gl::Offset &offset,
const gl::InternalFormat &formatInfo,
const gl::PixelUnpackState &unpack,
GLenum type,
const uint8_t *pixels)
{
GLuint inputRowPitch = 0;
ANGLE_VK_CHECK_MATH(contextVk, formatInfo.computeRowPitch(type, extents.width, unpack.alignment,
unpack.rowLength, &inputRowPitch));
GLuint inputDepthPitch = 0;
ANGLE_VK_CHECK_MATH(contextVk, formatInfo.computeDepthPitch(extents.height, unpack.imageHeight,
inputRowPitch, &inputDepthPitch));
// Note: skip images for 3D Textures.
ASSERT(!index.usesTex3D());
bool applySkipImages = false;
GLuint inputSkipBytes = 0;
ANGLE_VK_CHECK_MATH(contextVk,
formatInfo.computeSkipBytes(type, 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;
VkBuffer bufferHandle = VK_NULL_HANDLE;
uint8_t *stagingPointer = nullptr;
VkDeviceSize stagingOffset = 0;
size_t allocationSize = outputDepthPitch * extents.depth;
ANGLE_TRY(mStagingBuffer.allocate(contextVk, allocationSize, &stagingPointer, &bufferHandle,
&stagingOffset, nullptr));
const uint8_t *source = pixels + inputSkipBytes;
LoadImageFunctionInfo loadFunction = vkFormat.textureLoadFunctions(type);
loadFunction.loadFunction(extents.width, extents.height, extents.depth, source, inputRowPitch,
inputDepthPitch, stagingPointer, outputRowPitch, outputDepthPitch);
VkBufferImageCopy copy = {};
copy.bufferOffset = stagingOffset;
copy.bufferRowLength = extents.width;
copy.bufferImageHeight = extents.height;
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy.imageSubresource.mipLevel = index.getLevelIndex();
copy.imageSubresource.baseArrayLayer = index.hasLayer() ? index.getLayerIndex() : 0;
copy.imageSubresource.layerCount = index.getLayerCount();
gl_vk::GetOffset(offset, &copy.imageOffset);
gl_vk::GetExtent(extents, &copy.imageExtent);
mSubresourceUpdates.emplace_back(bufferHandle, copy);
return angle::Result::Continue;
}
angle::Result ImageHelper::stageSubresourceUpdateAndGetData(ContextVk *contextVk,
size_t allocationSize,
const gl::ImageIndex &imageIndex,
const gl::Extents &extents,
const gl::Offset &offset,
uint8_t **destData)
{
VkBuffer bufferHandle;
VkDeviceSize stagingOffset = 0;
ANGLE_TRY(mStagingBuffer.allocate(contextVk, allocationSize, destData, &bufferHandle,
&stagingOffset, nullptr));
VkBufferImageCopy copy = {};
copy.bufferOffset = stagingOffset;
copy.bufferRowLength = extents.width;
copy.bufferImageHeight = extents.height;
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy.imageSubresource.mipLevel = imageIndex.getLevelIndex();
copy.imageSubresource.baseArrayLayer = imageIndex.hasLayer() ? imageIndex.getLayerIndex() : 0;
copy.imageSubresource.layerCount = imageIndex.getLayerCount();
gl_vk::GetOffset(offset, &copy.imageOffset);
gl_vk::GetExtent(extents, &copy.imageExtent);
mSubresourceUpdates.emplace_back(bufferHandle, copy);
return angle::Result::Continue;
}
angle::Result ImageHelper::stageSubresourceUpdateFromFramebuffer(
const gl::Context *context,
const gl::ImageIndex &index,
const gl::Rectangle &sourceArea,
const gl::Offset &dstOffset,
const gl::Extents &dstExtent,
const gl::InternalFormat &formatInfo,
FramebufferVk *framebufferVk)
{
ContextVk *contextVk = vk::GetImpl(context);
// If the extents and offset is outside the source image, we need to clip.
gl::Rectangle clippedRectangle;
const gl::Extents readExtents = framebufferVk->getReadImageExtents();
if (!ClipRectangle(sourceArea, gl::Rectangle(0, 0, readExtents.width, readExtents.height),
&clippedRectangle))
{
// Empty source area, nothing to do.
return angle::Result::Continue;
}
bool isViewportFlipEnabled = contextVk->isViewportFlipEnabledForDrawFBO();
if (isViewportFlipEnabled)
{
clippedRectangle.y = readExtents.height - clippedRectangle.y - clippedRectangle.height;
}
// 1- obtain a buffer handle to copy to
RendererVk *renderer = contextVk->getRenderer();
const vk::Format &vkFormat = renderer->getFormat(formatInfo.sizedInternalFormat);
const angle::Format &storageFormat = vkFormat.textureFormat();
LoadImageFunctionInfo loadFunction = vkFormat.textureLoadFunctions(formatInfo.type);
size_t outputRowPitch = storageFormat.pixelBytes * clippedRectangle.width;
size_t outputDepthPitch = outputRowPitch * clippedRectangle.height;
VkBuffer bufferHandle = VK_NULL_HANDLE;
uint8_t *stagingPointer = nullptr;
VkDeviceSize stagingOffset = 0;
// The destination is only one layer deep.
size_t allocationSize = outputDepthPitch;
ANGLE_TRY(mStagingBuffer.allocate(contextVk, allocationSize, &stagingPointer, &bufferHandle,
&stagingOffset, nullptr));
const angle::Format &copyFormat =
GetFormatFromFormatType(formatInfo.internalFormat, formatInfo.type);
PackPixelsParams params(clippedRectangle, copyFormat, static_cast<GLuint>(outputRowPitch),
isViewportFlipEnabled, nullptr, 0);
// 2- copy the source image region to the pixel buffer using a cpu readback
if (loadFunction.requiresConversion)
{
// When a conversion is required, we need to use the loadFunction to read from a temporary
// buffer instead so its an even slower path.
size_t bufferSize =
storageFormat.pixelBytes * clippedRectangle.width * clippedRectangle.height;
angle::MemoryBuffer *memoryBuffer = nullptr;
ANGLE_VK_CHECK_ALLOC(contextVk, context->getScratchBuffer(bufferSize, &memoryBuffer));
// Read into the scratch buffer
ANGLE_TRY(framebufferVk->readPixelsImpl(
contextVk, clippedRectangle, params, VK_IMAGE_ASPECT_COLOR_BIT,
framebufferVk->getColorReadRenderTarget(), memoryBuffer->data()));
// Load from scratch buffer to our pixel buffer
loadFunction.loadFunction(clippedRectangle.width, clippedRectangle.height, 1,
memoryBuffer->data(), outputRowPitch, 0, stagingPointer,
outputRowPitch, 0);
}
else
{
// We read directly from the framebuffer into our pixel buffer.
ANGLE_TRY(framebufferVk->readPixelsImpl(
contextVk, clippedRectangle, params, VK_IMAGE_ASPECT_COLOR_BIT,
framebufferVk->getColorReadRenderTarget(), stagingPointer));
}
// 3- enqueue the destination image subresource update
VkBufferImageCopy copyToImage = {};
copyToImage.bufferOffset = static_cast<VkDeviceSize>(stagingOffset);
copyToImage.bufferRowLength = 0; // Tightly packed data can be specified as 0.
copyToImage.bufferImageHeight = clippedRectangle.height;
copyToImage.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyToImage.imageSubresource.mipLevel = index.getLevelIndex();
copyToImage.imageSubresource.baseArrayLayer = index.hasLayer() ? index.getLayerIndex() : 0;
copyToImage.imageSubresource.layerCount = index.getLayerCount();
gl_vk::GetOffset(dstOffset, &copyToImage.imageOffset);
gl_vk::GetExtent(dstExtent, &copyToImage.imageExtent);
// 3- enqueue the destination image subresource update
mSubresourceUpdates.emplace_back(bufferHandle, copyToImage);
return angle::Result::Continue;
}
void ImageHelper::stageSubresourceUpdateFromImage(vk::ImageHelper *image,
const gl::ImageIndex &index,
const gl::Offset &destOffset,
const gl::Extents &extents)
{
VkImageCopy copyToImage = {};
copyToImage.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyToImage.srcSubresource.layerCount = index.getLayerCount();
copyToImage.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyToImage.dstSubresource.mipLevel = index.getLevelIndex();
copyToImage.dstSubresource.baseArrayLayer = index.hasLayer() ? index.getLayerIndex() : 0;
copyToImage.dstSubresource.layerCount = index.getLayerCount();
gl_vk::GetOffset(destOffset, &copyToImage.dstOffset);
gl_vk::GetExtent(extents, &copyToImage.extent);
mSubresourceUpdates.emplace_back(image, copyToImage);
}
angle::Result ImageHelper::allocateStagingMemory(ContextVk *contextVk,
size_t sizeInBytes,
uint8_t **ptrOut,
VkBuffer *handleOut,
VkDeviceSize *offsetOut,
bool *newBufferAllocatedOut)
{
return mStagingBuffer.allocate(contextVk, sizeInBytes, ptrOut, handleOut, offsetOut,
newBufferAllocatedOut);
}
angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
uint32_t levelCount,
vk::CommandBuffer *commandBuffer)
{
if (mSubresourceUpdates.empty())
{
return angle::Result::Continue;
}
RendererVk *renderer = contextVk->getRenderer();
ANGLE_TRY(mStagingBuffer.flush(contextVk));
std::vector<SubresourceUpdate> updatesToKeep;
for (SubresourceUpdate &update : mSubresourceUpdates)
{
ASSERT((update.updateSource == SubresourceUpdate::UpdateSource::Buffer &&
update.buffer.bufferHandle != VK_NULL_HANDLE) ||
(update.updateSource == SubresourceUpdate::UpdateSource::Image &&
update.image.image != nullptr && update.image.image->valid()));
const uint32_t updateMipLevel = update.dstSubresource().mipLevel;
// It's possible we've accumulated updates that are no longer applicable if the image has
// never been flushed but the image description has changed. Check if this level exist for
// this image.
if (updateMipLevel >= levelCount)
{
updatesToKeep.emplace_back(update);
continue;
}
// Conservatively flush all writes to the image. We could use a more restricted barrier.
// Do not move this above the for loop, otherwise multiple updates can have race conditions
// and not be applied correctly as seen in:
// dEQP-gles2.functional_texture_specification_texsubimage2d_align_2d* tests on Windows AMD
changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
commandBuffer);
if (update.updateSource == SubresourceUpdate::UpdateSource::Buffer)
{
commandBuffer->copyBufferToImage(update.buffer.bufferHandle, mImage, getCurrentLayout(),
1, &update.buffer.copyRegion);
}
else
{
// Note: currently, the staging images are only made through color attachment writes. If
// they were written to otherwise in the future, the src stage of this transition should
// be adjusted appropriately.
update.image.image->changeLayoutWithStages(
VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
commandBuffer);
update.image.image->addReadDependency(this);
commandBuffer->copyImage(update.image.image->getImage(),
update.image.image->getCurrentLayout(), mImage,
getCurrentLayout(), 1, &update.image.copyRegion);
}
update.release(renderer);
}
// Only remove the updates that were actually applied to the image.
mSubresourceUpdates = std::move(updatesToKeep);
if (mSubresourceUpdates.empty())
{
mStagingBuffer.releaseRetainedBuffers(contextVk->getRenderer());
}
else
{
WARN() << "Internal Vulkan buffer could not be released. This is likely due to having "
"extra images defined in the Texture.";
}
return angle::Result::Continue;
}
bool ImageHelper::hasStagedUpdates() const
{
return !mSubresourceUpdates.empty();
}
// ImageHelper::SubresourceUpdate implementation
ImageHelper::SubresourceUpdate::SubresourceUpdate()
: updateSource(UpdateSource::Buffer), buffer{VK_NULL_HANDLE}
{}
ImageHelper::SubresourceUpdate::SubresourceUpdate(VkBuffer bufferHandleIn,
const VkBufferImageCopy &copyRegionIn)
: updateSource(UpdateSource::Buffer), buffer{bufferHandleIn, copyRegionIn}
{}
ImageHelper::SubresourceUpdate::SubresourceUpdate(vk::ImageHelper *imageIn,
const VkImageCopy &copyRegionIn)
: updateSource(UpdateSource::Image), image{imageIn, copyRegionIn}
{}
ImageHelper::SubresourceUpdate::SubresourceUpdate(const SubresourceUpdate &other)
: updateSource(other.updateSource)
{
if (updateSource == UpdateSource::Buffer)
{
buffer = other.buffer;
}
else
{
image = other.image;
}
}
void ImageHelper::SubresourceUpdate::release(RendererVk *renderer)
{
if (updateSource == UpdateSource::Image)
{
image.image->releaseImage(renderer);
image.image->releaseStagingBuffer(renderer);
SafeDelete(image.image);
}
}
bool ImageHelper::SubresourceUpdate::isUpdateToLayerLevel(uint32_t layerIndex,
uint32_t levelIndex) const
{
const VkImageSubresourceLayers &dst = dstSubresource();
return dst.baseArrayLayer == layerIndex && dst.mipLevel == levelIndex;
}
// FramebufferHelper implementation.
FramebufferHelper::FramebufferHelper() : CommandGraphResource(CommandGraphResourceType::Framebuffer)
{}
......
......@@ -33,6 +33,7 @@ class DynamicBuffer : angle::NonCopyable
{
public:
DynamicBuffer(VkBufferUsageFlags usage, size_t minSize, bool hostVisible);
DynamicBuffer(DynamicBuffer &&other);
~DynamicBuffer();
// Init is called after the buffer creation so that the alignment can be specified later.
......@@ -477,6 +478,8 @@ class ImageHelper final : public CommandGraphResource
ImageHelper(ImageHelper &&other);
~ImageHelper() override;
void initStagingBuffer(RendererVk *renderer);
angle::Result init(Context *context,
gl::TextureType textureType,
const gl::Extents &extents,
......@@ -514,7 +517,8 @@ class ImageHelper final : public CommandGraphResource
VkImageUsageFlags usage,
uint32_t layerCount);
void release(RendererVk *renderer);
void releaseImage(RendererVk *renderer);
void releaseStagingBuffer(RendererVk *renderer);
bool valid() const { return mImage.valid(); }
......@@ -573,7 +577,94 @@ class ImageHelper final : public CommandGraphResource
angle::Result generateMipmapsWithBlit(ContextVk *contextVk, GLuint maxLevel);
// Data staging
void removeStagedUpdates(RendererVk *renderer, const gl::ImageIndex &index);
angle::Result stageSubresourceUpdate(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Extents &extents,
const gl::Offset &offset,
const gl::InternalFormat &formatInfo,
const gl::PixelUnpackState &unpack,
GLenum type,
const uint8_t *pixels);
angle::Result stageSubresourceUpdateAndGetData(ContextVk *contextVk,
size_t allocationSize,
const gl::ImageIndex &imageIndex,
const gl::Extents &extents,
const gl::Offset &offset,
uint8_t **destData);
angle::Result stageSubresourceUpdateFromFramebuffer(const gl::Context *context,
const gl::ImageIndex &index,
const gl::Rectangle &sourceArea,
const gl::Offset &dstOffset,
const gl::Extents &dstExtent,
const gl::InternalFormat &formatInfo,
FramebufferVk *framebufferVk);
void stageSubresourceUpdateFromImage(vk::ImageHelper *image,
const gl::ImageIndex &index,
const gl::Offset &destOffset,
const gl::Extents &extents);
// This will use the underlying dynamic buffer to allocate some memory to be used as a src or
// dst.
angle::Result allocateStagingMemory(ContextVk *contextVk,
size_t sizeInBytes,
uint8_t **ptrOut,
VkBuffer *handleOut,
VkDeviceSize *offsetOut,
bool *newBufferAllocatedOut);
angle::Result flushStagedUpdates(ContextVk *contextVk,
uint32_t levelCount,
vk::CommandBuffer *commandBuffer);
bool hasStagedUpdates() const;
private:
struct SubresourceUpdate
{
SubresourceUpdate();
SubresourceUpdate(VkBuffer bufferHandle, const VkBufferImageCopy &copyRegion);
SubresourceUpdate(vk::ImageHelper *image, const VkImageCopy &copyRegion);
SubresourceUpdate(const SubresourceUpdate &other);
void release(RendererVk *renderer);
const VkImageSubresourceLayers &dstSubresource() const
{
return updateSource == UpdateSource::Buffer ? buffer.copyRegion.imageSubresource
: image.copyRegion.dstSubresource;
}
bool isUpdateToLayerLevel(uint32_t layerIndex, uint32_t levelIndex) const;
enum class UpdateSource
{
Buffer,
Image,
};
struct BufferUpdate
{
VkBuffer bufferHandle;
VkBufferImageCopy copyRegion;
};
struct ImageUpdate
{
vk::ImageHelper *image;
VkImageCopy copyRegion;
};
UpdateSource updateSource;
union
{
BufferUpdate buffer;
ImageUpdate image;
};
};
// Vulkan objects.
Image mImage;
DeviceMemory mDeviceMemory;
......@@ -589,6 +680,10 @@ class ImageHelper final : public CommandGraphResource
// Cached properties.
uint32_t mLayerCount;
uint32_t mLevelCount;
// Staging buffer
vk::DynamicBuffer mStagingBuffer;
std::vector<SubresourceUpdate> mSubresourceUpdates;
};
class FramebufferHelper : public CommandGraphResource
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment