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)
ASSERT(mCurrentPipeline.valid());
}
VkDevice device = mRenderer->getDevice();
const auto &state = mState.getState();
const auto &programGL = state.getProgram();
ProgramVk *programVk = GetImplAs<ProgramVk>(programGL);
......@@ -326,7 +325,7 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode)
vk::CommandBuffer *commandBuffer = nullptr;
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->bindVertexBuffers(0, maxAttrib, vertexHandles.data(),
......
......@@ -81,20 +81,12 @@ FramebufferVk *FramebufferVk::CreateDefaultFBO(const gl::FramebufferState &state
}
FramebufferVk::FramebufferVk(const gl::FramebufferState &state)
: FramebufferImpl(state),
mBackbuffer(nullptr),
mRenderPass(),
mFramebuffer(),
mInRenderPass(false)
: FramebufferImpl(state), mBackbuffer(nullptr), mRenderPass(), mFramebuffer()
{
}
FramebufferVk::FramebufferVk(const gl::FramebufferState &state, WindowSurfaceVk *backbuffer)
: FramebufferImpl(state),
mBackbuffer(backbuffer),
mRenderPass(),
mFramebuffer(),
mInRenderPass(false)
: FramebufferImpl(state), mBackbuffer(backbuffer), mRenderPass(), mFramebuffer()
{
}
......@@ -104,10 +96,9 @@ FramebufferVk::~FramebufferVk()
void FramebufferVk::destroy(const gl::Context *context)
{
ASSERT(!mInRenderPass);
VkDevice device = GetImplAs<ContextVk>(context)->getDevice();
// TODO(jmadill): Deferred deletion.
mRenderPass.destroy(device);
mFramebuffer.destroy(device);
}
......@@ -290,7 +281,7 @@ gl::Error FramebufferVk::readPixels(const gl::Context *context,
ANGLE_TRY(contextVk->getStartedCommandBuffer(&commandBuffer));
// End render pass if we're in one.
endRenderPass(commandBuffer);
renderer->endRenderPass();
stagingImage.getImage().changeLayoutTop(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_GENERAL,
commandBuffer);
......@@ -557,19 +548,11 @@ gl::Error FramebufferVk::getSamplePosition(size_t index, GLfloat *xy) const
return gl::InternalError() << "getSamplePosition is unimplemented.";
}
gl::Error FramebufferVk::ensureInRenderPass(const gl::Context *context,
VkDevice device,
vk::CommandBuffer *commandBuffer,
Serial queueSerial,
const gl::State &glState)
gl::Error FramebufferVk::beginRenderPass(const gl::Context *context,
VkDevice device,
vk::CommandBuffer *commandBuffer,
Serial queueSerial)
{
if (mInRenderPass)
{
return gl::NoError();
}
mInRenderPass = true;
// TODO(jmadill): Cache render targets.
for (const auto &colorAttachment : mState.getColorAttachments())
{
......@@ -598,6 +581,7 @@ gl::Error FramebufferVk::ensureInRenderPass(const gl::Context *context,
ASSERT(renderPass && renderPass->valid());
// TODO(jmadill): Proper clear value implementation.
const gl::State &glState = context->getGLState();
VkClearColorValue colorClear;
memset(&colorClear, 0, sizeof(VkClearColorValue));
colorClear.float32[0] = glState.getColorClearValue().red;
......@@ -627,13 +611,4 @@ gl::Error FramebufferVk::ensureInRenderPass(const gl::Context *context,
return gl::NoError();
}
void FramebufferVk::endRenderPass(vk::CommandBuffer *commandBuffer)
{
if (mInRenderPass)
{
commandBuffer->endRenderPass();
mInRenderPass = false;
}
}
} // namespace rx
......@@ -83,14 +83,10 @@ class FramebufferVk : public FramebufferImpl, public ResourceVk
gl::Error getSamplePosition(size_t index, GLfloat *xy) const override;
gl::Error ensureInRenderPass(const gl::Context *context,
VkDevice device,
vk::CommandBuffer *commandBuffer,
Serial queueSerial,
const gl::State &glState);
void endRenderPass(vk::CommandBuffer *commandBuffer);
bool isInRenderPass() const { return mInRenderPass; }
gl::Error beginRenderPass(const gl::Context *context,
VkDevice device,
vk::CommandBuffer *commandBuffer,
Serial queueSerial);
gl::ErrorOrResult<vk::RenderPass *> getRenderPass(const gl::Context *context, VkDevice device);
......@@ -105,7 +101,6 @@ class FramebufferVk : public FramebufferImpl, public ResourceVk
vk::RenderPass mRenderPass;
vk::Framebuffer mFramebuffer;
bool mInRenderPass;
};
} // namespace rx
......
......@@ -96,7 +96,8 @@ RendererVk::RendererVk()
mGlslangWrapper(nullptr),
mLastCompletedQueueSerial(mQueueSerialFactory.generate()),
mCurrentQueueSerial(mQueueSerialFactory.generate()),
mInFlightCommands()
mInFlightCommands(),
mCurrentRenderPassFramebuffer(nullptr)
{
}
......@@ -785,4 +786,31 @@ Serial RendererVk::getCurrentQueueSerial() const
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
......@@ -24,6 +24,7 @@ class AttributeMap;
namespace rx
{
class FramebufferVk;
class GlslangWrapper;
namespace vk
......@@ -97,6 +98,10 @@ class RendererVk : angle::NonCopyable
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:
void ensureCapsInitialized() const;
void generateCaps(gl::Caps *outCaps,
......@@ -135,6 +140,9 @@ class RendererVk : angle::NonCopyable
std::vector<vk::FenceAndSerial> mInFlightFences;
std::vector<vk::GarbageObject> mGarbage;
vk::MemoryProperties mMemoryProperties;
// TODO(jmadill): Don't keep a single renderpass in the Renderer.
FramebufferVk *mCurrentRenderPassFramebuffer;
};
} // namespace rx
......
......@@ -426,8 +426,7 @@ egl::Error WindowSurfaceVk::swap(const gl::Context *context)
ANGLE_TRY(renderer->getStartedCommandBuffer(&currentCB));
// End render pass
FramebufferVk *framebufferVk = GetImplAs<FramebufferVk>(mState.defaultFramebuffer);
framebufferVk->endRenderPass(currentCB);
renderer->endRenderPass();
auto &image = mSwapchainImages[mCurrentSwapchainImageIndex];
......
......@@ -49,11 +49,25 @@ gl::Error TextureVk::setImage(const gl::Context *context,
const gl::PixelUnpackState &unpack,
const uint8_t *pixels)
{
ContextVk *contextVk = GetImplAs<ContextVk>(context);
RendererVk *renderer = contextVk->getRenderer();
// TODO(jmadill): support multi-level textures.
ASSERT(level == 0);
// TODO(jmadill): support texture re-definition.
ASSERT(!mImage.valid());
if (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.
ASSERT(target == GL_TEXTURE_2D);
......@@ -62,93 +76,99 @@ gl::Error TextureVk::setImage(const gl::Context *context,
const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat, type);
const vk::Format &vkFormat = vk::Format::Get(formatInfo.sizedInternalFormat);
VkImageCreateInfo imageInfo;
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.pNext = nullptr;
imageInfo.flags = 0;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.format = vkFormat.native;
imageInfo.extent.width = size.width;
imageInfo.extent.height = size.height;
imageInfo.extent.depth = size.depth;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
// TODO(jmadill): Are all these image transfer bits necessary?
imageInfo.usage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.queueFamilyIndexCount = 0;
imageInfo.pQueueFamilyIndices = nullptr;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ContextVk *contextVk = GetImplAs<ContextVk>(context);
VkDevice device = contextVk->getDevice();
ANGLE_TRY(mImage.init(device, imageInfo));
// Allocate the device memory for the image.
// TODO(jmadill): Use more intelligent device memory allocation.
VkMemoryRequirements memoryRequirements;
mImage.getMemoryRequirements(device, &memoryRequirements);
VkDevice device = contextVk->getDevice();
RendererVk *renderer = contextVk->getRenderer();
if (!mImage.valid())
{
ASSERT(!mDeviceMemory.valid() && !mImageView.valid());
VkImageCreateInfo imageInfo;
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.pNext = nullptr;
imageInfo.flags = 0;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.format = vkFormat.native;
imageInfo.extent.width = size.width;
imageInfo.extent.height = size.height;
imageInfo.extent.depth = size.depth;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
// TODO(jmadill): Are all these image transfer bits necessary?
imageInfo.usage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.queueFamilyIndexCount = 0;
imageInfo.pQueueFamilyIndices = nullptr;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ANGLE_TRY(mImage.init(device, imageInfo));
// Allocate the device memory for the image.
// TODO(jmadill): Use more intelligent device memory allocation.
VkMemoryRequirements memoryRequirements;
mImage.getMemoryRequirements(device, &memoryRequirements);
uint32_t memoryIndex = renderer->getMemoryProperties().findCompatibleMemoryIndex(
memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VkMemoryAllocateInfo allocateInfo;
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocateInfo.pNext = nullptr;
allocateInfo.allocationSize = memoryRequirements.size;
allocateInfo.memoryTypeIndex = memoryIndex;
ANGLE_TRY(mDeviceMemory.allocate(device, allocateInfo));
ANGLE_TRY(mImage.bindMemory(device, mDeviceMemory));
VkImageViewCreateInfo viewInfo;
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.pNext = nullptr;
viewInfo.flags = 0;
viewInfo.image = mImage.getHandle();
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = vkFormat.native;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
ANGLE_TRY(mImageView.init(device, viewInfo));
}
uint32_t memoryIndex = renderer->getMemoryProperties().findCompatibleMemoryIndex(
memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VkMemoryAllocateInfo allocateInfo;
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocateInfo.pNext = nullptr;
allocateInfo.allocationSize = memoryRequirements.size;
allocateInfo.memoryTypeIndex = memoryIndex;
ANGLE_TRY(mDeviceMemory.allocate(device, allocateInfo));
ANGLE_TRY(mImage.bindMemory(device, mDeviceMemory));
VkImageViewCreateInfo viewInfo;
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.pNext = nullptr;
viewInfo.flags = 0;
viewInfo.image = mImage.getHandle();
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = vkFormat.native;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
ANGLE_TRY(mImageView.init(device, viewInfo));
// Create a simple sampler. Force basic parameter settings.
// TODO(jmadill): Sampler parameters.
VkSamplerCreateInfo samplerInfo;
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.pNext = nullptr;
samplerInfo.flags = 0;
samplerInfo.magFilter = VK_FILTER_NEAREST;
samplerInfo.minFilter = VK_FILTER_NEAREST;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.0f;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 1.0f;
samplerInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
ANGLE_TRY(mSampler.init(device, samplerInfo));
if (!mSampler.valid())
{
// Create a simple sampler. Force basic parameter settings.
// TODO(jmadill): Sampler parameters.
VkSamplerCreateInfo samplerInfo;
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.pNext = nullptr;
samplerInfo.flags = 0;
samplerInfo.magFilter = VK_FILTER_NEAREST;
samplerInfo.minFilter = VK_FILTER_NEAREST;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.0f;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 1.0f;
samplerInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
ANGLE_TRY(mSampler.init(device, samplerInfo));
}
mRenderTarget.image = &mImage;
mRenderTarget.imageView = &mImageView;
......@@ -212,6 +232,10 @@ gl::Error TextureVk::setImage(const gl::Context *context,
ANGLE_TRY(contextVk->getStartedCommandBuffer(&commandBuffer));
setQueueSerial(renderer->getCurrentQueueSerial());
// Ensure we aren't in a render pass.
// TODO(jmadill): Command reordering.
renderer->endRenderPass();
stagingImage.getImage().changeLayoutTop(
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,
......
......@@ -832,6 +832,88 @@ TEST_P(SimpleStateChangeTest, RedefineBufferInUse)
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
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