Commit 5b18f487 by Jamie Madill Committed by Commit Bot

Vulkan: Implement basic TexSubImage2D.

This also adds a test for updating a Texture that is in-use. This will ensure our Texture updates occur at the right time when we're implementing command re-ordering. Bug: angleproject:2264 Bug: angleproject:2200 Change-Id: Id6040d7238eca031e3cc7b27564d8ea815bf3d73 Reviewed-on: https://chromium-review.googlesource.com/801031 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarFrank Henigman <fjhenigman@chromium.org>
parent 37584b36
...@@ -180,74 +180,7 @@ gl::Error TextureVk::setImage(const gl::Context *context, ...@@ -180,74 +180,7 @@ gl::Error TextureVk::setImage(const gl::Context *context,
// TODO(jmadill): Consider re-using staging texture. // TODO(jmadill): Consider re-using staging texture.
if (pixels) if (pixels)
{ {
vk::StagingImage stagingImage; ANGLE_TRY(setSubImageImpl(contextVk, formatInfo, unpack, type, pixels));
ANGLE_TRY(renderer->createStagingImage(TextureDimension::TEX_2D, vkFormat, size,
vk::StagingUsage::Write, &stagingImage));
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 this structure based on input parameters.
VkImageSubresource subresource;
subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource.mipLevel = 0;
subresource.arrayLayer = 0;
VkSubresourceLayout subresourceLayout;
vkGetImageSubresourceLayout(device, stagingImage.getImage().getHandle(), &subresource,
&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::CommandBufferAndState *commandBuffer = nullptr;
ANGLE_TRY(contextVk->getStartedCommandBuffer(&commandBuffer));
setQueueSerial(renderer->getCurrentQueueSerial());
// Ensure we aren't in a render pass.
// TODO(jmadill): Command reordering.
renderer->endRenderPass();
stagingImage.getImage().changeLayoutWithStages(
VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer);
mImage.changeLayoutWithStages(
VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer);
gl::Box wholeRegion(0, 0, 0, size.width, size.height, size.depth);
commandBuffer->copySingleImage(stagingImage.getImage(), mImage, wholeRegion,
VK_IMAGE_ASPECT_COLOR_BIT);
// TODO(jmadill): Re-use staging images.
renderer->releaseObject(renderer->getCurrentQueueSerial(), &stagingImage);
} }
return gl::NoError(); return gl::NoError();
...@@ -262,8 +195,91 @@ gl::Error TextureVk::setSubImage(const gl::Context *context, ...@@ -262,8 +195,91 @@ gl::Error TextureVk::setSubImage(const gl::Context *context,
const gl::PixelUnpackState &unpack, const gl::PixelUnpackState &unpack,
const uint8_t *pixels) const uint8_t *pixels)
{ {
UNIMPLEMENTED(); ContextVk *contextVk = vk::GetImpl(context);
return gl::InternalError(); 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 = mRenderTarget.extents;
const vk::Format &vkFormat = *mRenderTarget.format;
vk::StagingImage stagingImage;
ANGLE_TRY(renderer->createStagingImage(TextureDimension::TEX_2D, vkFormat, size,
vk::StagingUsage::Write, &stagingImage));
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 this structure based on input parameters.
VkImageSubresource subresource;
subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource.mipLevel = 0;
subresource.arrayLayer = 0;
VkSubresourceLayout subresourceLayout;
vkGetImageSubresourceLayout(device, stagingImage.getImage().getHandle(), &subresource,
&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::CommandBufferAndState *commandBuffer = nullptr;
ANGLE_TRY(contextVk->getStartedCommandBuffer(&commandBuffer));
setQueueSerial(renderer->getCurrentQueueSerial());
// Ensure we aren't in a render pass.
// TODO(jmadill): Command reordering.
renderer->endRenderPass();
stagingImage.getImage().changeLayoutWithStages(
VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer);
mImage.changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer);
gl::Box wholeRegion(0, 0, 0, size.width, size.height, size.depth);
commandBuffer->copySingleImage(stagingImage.getImage(), mImage, wholeRegion,
VK_IMAGE_ASPECT_COLOR_BIT);
// TODO(jmadill): Re-use staging images.
renderer->releaseObject(renderer->getCurrentQueueSerial(), &stagingImage);
return gl::NoError();
} }
gl::Error TextureVk::setCompressedImage(const gl::Context *context, gl::Error TextureVk::setCompressedImage(const gl::Context *context,
......
...@@ -116,6 +116,12 @@ class TextureVk : public TextureImpl, public ResourceVk ...@@ -116,6 +116,12 @@ class TextureVk : public TextureImpl, public ResourceVk
const vk::Sampler &getSampler() const; const vk::Sampler &getSampler() const;
private: private:
gl::Error setSubImageImpl(ContextVk *contextVk,
const gl::InternalFormat &formatInfo,
const gl::PixelUnpackState &unpack,
GLenum type,
const uint8_t *pixels);
// TODO(jmadill): support a more flexible storage back-end. // TODO(jmadill): support a more flexible storage back-end.
vk::Image mImage; vk::Image mImage;
vk::DeviceMemory mDeviceMemory; vk::DeviceMemory mDeviceMemory;
......
...@@ -950,6 +950,44 @@ TEST_P(SimpleStateChangeTest, RedefineTextureInUse) ...@@ -950,6 +950,44 @@ TEST_P(SimpleStateChangeTest, RedefineTextureInUse)
EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::white); EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::white);
} }
// Test updating a Texture's contents while in use by GL works as expected.
TEST_P(SimpleStateChangeTest, UpdateTextureInUse)
{
std::array<GLColor, 4> rgby = {{GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow}};
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgby.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Draw RGBY to the Framebuffer. The texture is now in-use by GL.
draw2DTexturedQuad(0.5f, 1.0f, true);
// Update the texture to be YBGR, while the Texture is in-use. Should not affect the draw.
std::array<GLColor, 4> ybgr = {{GLColor::yellow, GLColor::blue, GLColor::green, GLColor::red}};
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, ybgr.data());
ASSERT_GL_NO_ERROR();
// Check the Framebuffer. The draw call should have completed with the original RGBY data.
int w = getWindowWidth() - 2;
int h = getWindowHeight() - 2;
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(w, 0, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(0, h, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::yellow);
// Draw again to the Framebuffer. The second draw call should use the updated YBGR data.
draw2DTexturedQuad(0.5f, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(w, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(0, h, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::red);
ASSERT_GL_NO_ERROR();
}
const char kSolidColorVertexShader[] = R"(attribute vec2 position; const char kSolidColorVertexShader[] = R"(attribute vec2 position;
void main() void main()
{ {
......
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