Commit 1b038241 by Jamie Madill

Vulkan: Support Texture redefinition.

Because initializing the texture can queue a copy from a staging vk::Image, we must ensure we're not in a render pass. To make this easier we move the current render pass tracking into the RendererVk from the FramebufferVk class. (Note: in the future we will have deferred command submission and this will become unnecessary.) BUG=angleproject:2200 Change-Id: Ide8d4d70b50efbd79bbfa7006ad75cbc57cdf4c7 Reviewed-on: https://chromium-review.googlesource.com/741549 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 91ab54b6
...@@ -307,7 +307,6 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode) ...@@ -307,7 +307,6 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode)
ASSERT(mCurrentPipeline.valid()); ASSERT(mCurrentPipeline.valid());
} }
VkDevice device = mRenderer->getDevice();
const auto &state = mState.getState(); const auto &state = mState.getState();
const auto &programGL = state.getProgram(); const auto &programGL = state.getProgram();
ProgramVk *programVk = GetImplAs<ProgramVk>(programGL); ProgramVk *programVk = GetImplAs<ProgramVk>(programGL);
...@@ -326,7 +325,7 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode) ...@@ -326,7 +325,7 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode)
vk::CommandBuffer *commandBuffer = nullptr; vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(mRenderer->getStartedCommandBuffer(&commandBuffer)); ANGLE_TRY(mRenderer->getStartedCommandBuffer(&commandBuffer));
ANGLE_TRY(vkFBO->ensureInRenderPass(context, device, commandBuffer, queueSerial, state)); ANGLE_TRY(mRenderer->ensureInRenderPass(context, vkFBO));
commandBuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mCurrentPipeline); commandBuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mCurrentPipeline);
commandBuffer->bindVertexBuffers(0, maxAttrib, vertexHandles.data(), commandBuffer->bindVertexBuffers(0, maxAttrib, vertexHandles.data(),
......
...@@ -81,20 +81,12 @@ FramebufferVk *FramebufferVk::CreateDefaultFBO(const gl::FramebufferState &state ...@@ -81,20 +81,12 @@ FramebufferVk *FramebufferVk::CreateDefaultFBO(const gl::FramebufferState &state
} }
FramebufferVk::FramebufferVk(const gl::FramebufferState &state) FramebufferVk::FramebufferVk(const gl::FramebufferState &state)
: FramebufferImpl(state), : FramebufferImpl(state), mBackbuffer(nullptr), mRenderPass(), mFramebuffer()
mBackbuffer(nullptr),
mRenderPass(),
mFramebuffer(),
mInRenderPass(false)
{ {
} }
FramebufferVk::FramebufferVk(const gl::FramebufferState &state, WindowSurfaceVk *backbuffer) FramebufferVk::FramebufferVk(const gl::FramebufferState &state, WindowSurfaceVk *backbuffer)
: FramebufferImpl(state), : FramebufferImpl(state), mBackbuffer(backbuffer), mRenderPass(), mFramebuffer()
mBackbuffer(backbuffer),
mRenderPass(),
mFramebuffer(),
mInRenderPass(false)
{ {
} }
...@@ -104,10 +96,9 @@ FramebufferVk::~FramebufferVk() ...@@ -104,10 +96,9 @@ FramebufferVk::~FramebufferVk()
void FramebufferVk::destroy(const gl::Context *context) void FramebufferVk::destroy(const gl::Context *context)
{ {
ASSERT(!mInRenderPass);
VkDevice device = GetImplAs<ContextVk>(context)->getDevice(); VkDevice device = GetImplAs<ContextVk>(context)->getDevice();
// TODO(jmadill): Deferred deletion.
mRenderPass.destroy(device); mRenderPass.destroy(device);
mFramebuffer.destroy(device); mFramebuffer.destroy(device);
} }
...@@ -290,7 +281,7 @@ gl::Error FramebufferVk::readPixels(const gl::Context *context, ...@@ -290,7 +281,7 @@ gl::Error FramebufferVk::readPixels(const gl::Context *context,
ANGLE_TRY(contextVk->getStartedCommandBuffer(&commandBuffer)); ANGLE_TRY(contextVk->getStartedCommandBuffer(&commandBuffer));
// End render pass if we're in one. // End render pass if we're in one.
endRenderPass(commandBuffer); renderer->endRenderPass();
stagingImage.getImage().changeLayoutTop(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_GENERAL, stagingImage.getImage().changeLayoutTop(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_GENERAL,
commandBuffer); commandBuffer);
...@@ -557,19 +548,11 @@ gl::Error FramebufferVk::getSamplePosition(size_t index, GLfloat *xy) const ...@@ -557,19 +548,11 @@ gl::Error FramebufferVk::getSamplePosition(size_t index, GLfloat *xy) const
return gl::InternalError() << "getSamplePosition is unimplemented."; return gl::InternalError() << "getSamplePosition is unimplemented.";
} }
gl::Error FramebufferVk::ensureInRenderPass(const gl::Context *context, gl::Error FramebufferVk::beginRenderPass(const gl::Context *context,
VkDevice device, VkDevice device,
vk::CommandBuffer *commandBuffer, vk::CommandBuffer *commandBuffer,
Serial queueSerial, Serial queueSerial)
const gl::State &glState)
{ {
if (mInRenderPass)
{
return gl::NoError();
}
mInRenderPass = true;
// TODO(jmadill): Cache render targets. // TODO(jmadill): Cache render targets.
for (const auto &colorAttachment : mState.getColorAttachments()) for (const auto &colorAttachment : mState.getColorAttachments())
{ {
...@@ -598,6 +581,7 @@ gl::Error FramebufferVk::ensureInRenderPass(const gl::Context *context, ...@@ -598,6 +581,7 @@ gl::Error FramebufferVk::ensureInRenderPass(const gl::Context *context,
ASSERT(renderPass && renderPass->valid()); ASSERT(renderPass && renderPass->valid());
// TODO(jmadill): Proper clear value implementation. // TODO(jmadill): Proper clear value implementation.
const gl::State &glState = context->getGLState();
VkClearColorValue colorClear; VkClearColorValue colorClear;
memset(&colorClear, 0, sizeof(VkClearColorValue)); memset(&colorClear, 0, sizeof(VkClearColorValue));
colorClear.float32[0] = glState.getColorClearValue().red; colorClear.float32[0] = glState.getColorClearValue().red;
...@@ -627,13 +611,4 @@ gl::Error FramebufferVk::ensureInRenderPass(const gl::Context *context, ...@@ -627,13 +611,4 @@ gl::Error FramebufferVk::ensureInRenderPass(const gl::Context *context,
return gl::NoError(); return gl::NoError();
} }
void FramebufferVk::endRenderPass(vk::CommandBuffer *commandBuffer)
{
if (mInRenderPass)
{
commandBuffer->endRenderPass();
mInRenderPass = false;
}
}
} // namespace rx } // namespace rx
...@@ -83,14 +83,10 @@ class FramebufferVk : public FramebufferImpl, public ResourceVk ...@@ -83,14 +83,10 @@ class FramebufferVk : public FramebufferImpl, public ResourceVk
gl::Error getSamplePosition(size_t index, GLfloat *xy) const override; gl::Error getSamplePosition(size_t index, GLfloat *xy) const override;
gl::Error ensureInRenderPass(const gl::Context *context, gl::Error beginRenderPass(const gl::Context *context,
VkDevice device, VkDevice device,
vk::CommandBuffer *commandBuffer, vk::CommandBuffer *commandBuffer,
Serial queueSerial, Serial queueSerial);
const gl::State &glState);
void endRenderPass(vk::CommandBuffer *commandBuffer);
bool isInRenderPass() const { return mInRenderPass; }
gl::ErrorOrResult<vk::RenderPass *> getRenderPass(const gl::Context *context, VkDevice device); gl::ErrorOrResult<vk::RenderPass *> getRenderPass(const gl::Context *context, VkDevice device);
...@@ -105,7 +101,6 @@ class FramebufferVk : public FramebufferImpl, public ResourceVk ...@@ -105,7 +101,6 @@ class FramebufferVk : public FramebufferImpl, public ResourceVk
vk::RenderPass mRenderPass; vk::RenderPass mRenderPass;
vk::Framebuffer mFramebuffer; vk::Framebuffer mFramebuffer;
bool mInRenderPass;
}; };
} // namespace rx } // namespace rx
......
...@@ -96,7 +96,8 @@ RendererVk::RendererVk() ...@@ -96,7 +96,8 @@ RendererVk::RendererVk()
mGlslangWrapper(nullptr), mGlslangWrapper(nullptr),
mLastCompletedQueueSerial(mQueueSerialFactory.generate()), mLastCompletedQueueSerial(mQueueSerialFactory.generate()),
mCurrentQueueSerial(mQueueSerialFactory.generate()), mCurrentQueueSerial(mQueueSerialFactory.generate()),
mInFlightCommands() mInFlightCommands(),
mCurrentRenderPassFramebuffer(nullptr)
{ {
} }
...@@ -785,4 +786,31 @@ Serial RendererVk::getCurrentQueueSerial() const ...@@ -785,4 +786,31 @@ Serial RendererVk::getCurrentQueueSerial() const
return mCurrentQueueSerial; return mCurrentQueueSerial;
} }
gl::Error RendererVk::ensureInRenderPass(const gl::Context *context, FramebufferVk *framebufferVk)
{
if (mCurrentRenderPassFramebuffer == framebufferVk)
{
return gl::NoError();
}
if (mCurrentRenderPassFramebuffer)
{
endRenderPass();
}
ANGLE_TRY(
framebufferVk->beginRenderPass(context, mDevice, &mCommandBuffer, mCurrentQueueSerial));
mCurrentRenderPassFramebuffer = framebufferVk;
return gl::NoError();
}
void RendererVk::endRenderPass()
{
if (mCurrentRenderPassFramebuffer)
{
ASSERT(mCommandBuffer.started());
mCommandBuffer.endRenderPass();
mCurrentRenderPassFramebuffer = nullptr;
}
}
} // namespace rx } // namespace rx
...@@ -24,6 +24,7 @@ class AttributeMap; ...@@ -24,6 +24,7 @@ class AttributeMap;
namespace rx namespace rx
{ {
class FramebufferVk;
class GlslangWrapper; class GlslangWrapper;
namespace vk namespace vk
...@@ -97,6 +98,10 @@ class RendererVk : angle::NonCopyable ...@@ -97,6 +98,10 @@ class RendererVk : angle::NonCopyable
const vk::MemoryProperties &getMemoryProperties() const { return mMemoryProperties; } const vk::MemoryProperties &getMemoryProperties() const { return mMemoryProperties; }
// TODO(jmadill): Don't keep a single renderpass in the Renderer.
gl::Error ensureInRenderPass(const gl::Context *context, FramebufferVk *framebufferVk);
void endRenderPass();
private: private:
void ensureCapsInitialized() const; void ensureCapsInitialized() const;
void generateCaps(gl::Caps *outCaps, void generateCaps(gl::Caps *outCaps,
...@@ -135,6 +140,9 @@ class RendererVk : angle::NonCopyable ...@@ -135,6 +140,9 @@ class RendererVk : angle::NonCopyable
std::vector<vk::FenceAndSerial> mInFlightFences; std::vector<vk::FenceAndSerial> mInFlightFences;
std::vector<vk::GarbageObject> mGarbage; std::vector<vk::GarbageObject> mGarbage;
vk::MemoryProperties mMemoryProperties; vk::MemoryProperties mMemoryProperties;
// TODO(jmadill): Don't keep a single renderpass in the Renderer.
FramebufferVk *mCurrentRenderPassFramebuffer;
}; };
} // namespace rx } // namespace rx
......
...@@ -426,8 +426,7 @@ egl::Error WindowSurfaceVk::swap(const gl::Context *context) ...@@ -426,8 +426,7 @@ egl::Error WindowSurfaceVk::swap(const gl::Context *context)
ANGLE_TRY(renderer->getStartedCommandBuffer(&currentCB)); ANGLE_TRY(renderer->getStartedCommandBuffer(&currentCB));
// End render pass // End render pass
FramebufferVk *framebufferVk = GetImplAs<FramebufferVk>(mState.defaultFramebuffer); renderer->endRenderPass();
framebufferVk->endRenderPass(currentCB);
auto &image = mSwapchainImages[mCurrentSwapchainImageIndex]; auto &image = mSwapchainImages[mCurrentSwapchainImageIndex];
......
...@@ -49,11 +49,25 @@ gl::Error TextureVk::setImage(const gl::Context *context, ...@@ -49,11 +49,25 @@ gl::Error TextureVk::setImage(const gl::Context *context,
const gl::PixelUnpackState &unpack, const gl::PixelUnpackState &unpack,
const uint8_t *pixels) const uint8_t *pixels)
{ {
ContextVk *contextVk = GetImplAs<ContextVk>(context);
RendererVk *renderer = contextVk->getRenderer();
// TODO(jmadill): support multi-level textures. // TODO(jmadill): support multi-level textures.
ASSERT(level == 0); ASSERT(level == 0);
// TODO(jmadill): support texture re-definition. if (mImage.valid())
ASSERT(!mImage.valid()); {
const gl::ImageDesc &desc = mState.getImageDesc(target, level);
// TODO(jmadill): Consider comparing stored vk::Format.
if (desc.size != size ||
!gl::Format::SameSized(desc.format, gl::Format(internalFormat, type)))
{
renderer->releaseResource(*this, &mImage);
renderer->releaseResource(*this, &mDeviceMemory);
renderer->releaseResource(*this, &mImageView);
}
}
// TODO(jmadill): support other types of textures. // TODO(jmadill): support other types of textures.
ASSERT(target == GL_TEXTURE_2D); ASSERT(target == GL_TEXTURE_2D);
...@@ -62,6 +76,12 @@ gl::Error TextureVk::setImage(const gl::Context *context, ...@@ -62,6 +76,12 @@ gl::Error TextureVk::setImage(const gl::Context *context,
const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat, type); const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat, type);
const vk::Format &vkFormat = vk::Format::Get(formatInfo.sizedInternalFormat); const vk::Format &vkFormat = vk::Format::Get(formatInfo.sizedInternalFormat);
VkDevice device = contextVk->getDevice();
if (!mImage.valid())
{
ASSERT(!mDeviceMemory.valid() && !mImageView.valid());
VkImageCreateInfo imageInfo; VkImageCreateInfo imageInfo;
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.pNext = nullptr; imageInfo.pNext = nullptr;
...@@ -84,8 +104,6 @@ gl::Error TextureVk::setImage(const gl::Context *context, ...@@ -84,8 +104,6 @@ gl::Error TextureVk::setImage(const gl::Context *context,
imageInfo.pQueueFamilyIndices = nullptr; imageInfo.pQueueFamilyIndices = nullptr;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ContextVk *contextVk = GetImplAs<ContextVk>(context);
VkDevice device = contextVk->getDevice();
ANGLE_TRY(mImage.init(device, imageInfo)); ANGLE_TRY(mImage.init(device, imageInfo));
// Allocate the device memory for the image. // Allocate the device memory for the image.
...@@ -93,8 +111,6 @@ gl::Error TextureVk::setImage(const gl::Context *context, ...@@ -93,8 +111,6 @@ gl::Error TextureVk::setImage(const gl::Context *context,
VkMemoryRequirements memoryRequirements; VkMemoryRequirements memoryRequirements;
mImage.getMemoryRequirements(device, &memoryRequirements); mImage.getMemoryRequirements(device, &memoryRequirements);
RendererVk *renderer = contextVk->getRenderer();
uint32_t memoryIndex = renderer->getMemoryProperties().findCompatibleMemoryIndex( uint32_t memoryIndex = renderer->getMemoryProperties().findCompatibleMemoryIndex(
memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
...@@ -125,7 +141,10 @@ gl::Error TextureVk::setImage(const gl::Context *context, ...@@ -125,7 +141,10 @@ gl::Error TextureVk::setImage(const gl::Context *context,
viewInfo.subresourceRange.layerCount = 1; viewInfo.subresourceRange.layerCount = 1;
ANGLE_TRY(mImageView.init(device, viewInfo)); ANGLE_TRY(mImageView.init(device, viewInfo));
}
if (!mSampler.valid())
{
// Create a simple sampler. Force basic parameter settings. // Create a simple sampler. Force basic parameter settings.
// TODO(jmadill): Sampler parameters. // TODO(jmadill): Sampler parameters.
VkSamplerCreateInfo samplerInfo; VkSamplerCreateInfo samplerInfo;
...@@ -149,6 +168,7 @@ gl::Error TextureVk::setImage(const gl::Context *context, ...@@ -149,6 +168,7 @@ gl::Error TextureVk::setImage(const gl::Context *context,
samplerInfo.unnormalizedCoordinates = VK_FALSE; samplerInfo.unnormalizedCoordinates = VK_FALSE;
ANGLE_TRY(mSampler.init(device, samplerInfo)); ANGLE_TRY(mSampler.init(device, samplerInfo));
}
mRenderTarget.image = &mImage; mRenderTarget.image = &mImage;
mRenderTarget.imageView = &mImageView; mRenderTarget.imageView = &mImageView;
...@@ -212,6 +232,10 @@ gl::Error TextureVk::setImage(const gl::Context *context, ...@@ -212,6 +232,10 @@ gl::Error TextureVk::setImage(const gl::Context *context,
ANGLE_TRY(contextVk->getStartedCommandBuffer(&commandBuffer)); ANGLE_TRY(contextVk->getStartedCommandBuffer(&commandBuffer));
setQueueSerial(renderer->getCurrentQueueSerial()); setQueueSerial(renderer->getCurrentQueueSerial());
// Ensure we aren't in a render pass.
// TODO(jmadill): Command reordering.
renderer->endRenderPass();
stagingImage.getImage().changeLayoutTop( stagingImage.getImage().changeLayoutTop(
VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, commandBuffer); VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, commandBuffer);
mImage.changeLayoutTop(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mImage.changeLayoutTop(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
......
...@@ -832,6 +832,88 @@ TEST_P(SimpleStateChangeTest, RedefineBufferInUse) ...@@ -832,6 +832,88 @@ TEST_P(SimpleStateChangeTest, RedefineBufferInUse)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
} }
// Tests that deleting an in-flight Texture does not immediately delete the resource.
TEST_P(SimpleStateChangeTest, DeleteTextureInUse)
{
std::array<GLColor, 4> colors = {
{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, colors.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
draw2DTexturedQuad(0.5f, 1.0f, true);
tex.reset();
EXPECT_GL_NO_ERROR();
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);
}
// Tests that redefining an in-flight Texture does not affect the in-flight resource.
TEST_P(SimpleStateChangeTest, RedefineTextureInUse)
{
std::array<GLColor, 4> colors = {
{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, colors.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Draw with the first texture.
draw2DTexturedQuad(0.5f, 1.0f, true);
// Redefine the in-flight texture.
constexpr int kBigSize = 32;
std::vector<GLColor> bigColors;
for (int y = 0; y < kBigSize; ++y)
{
for (int x = 0; x < kBigSize; ++x)
{
bool xComp = x < kBigSize / 2;
bool yComp = y < kBigSize / 2;
if (yComp)
{
bigColors.push_back(xComp ? GLColor::cyan : GLColor::magenta);
}
else
{
bigColors.push_back(xComp ? GLColor::yellow : GLColor::white);
}
}
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, bigColors.data());
EXPECT_GL_NO_ERROR();
// Verify the first draw had the correct data via ReadPixels.
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 and verify with the redefined data.
draw2DTexturedQuad(0.5f, 1.0f, true);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::cyan);
EXPECT_PIXEL_COLOR_EQ(w, 0, GLColor::magenta);
EXPECT_PIXEL_COLOR_EQ(0, h, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::white);
}
} // anonymous namespace } // anonymous namespace
ANGLE_INSTANTIATE_TEST(StateChangeTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL()); ANGLE_INSTANTIATE_TEST(StateChangeTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL());
......
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