Vulkan: Defer RenderPass image barriers.

We accumulate image barriers in two places:

 * for GL sampler textures
 * for GL framebuffer attachments (Render Targets)

Then we issue the barriers together in a single call before the RP.
This fixes a bug where we were missing a layout transition in some
cases when transitioning between a sampler and a render target. It
should also be faster to issue a single barrier before a RP than issue
several smaller barriers.

Bug: angleproject:3539
Bug: angleproject:4029
Change-Id: I180b770f0df6b44d209e5c618ba36bcc1c6372e4
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2044236
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Cody Northrop <cnorthrop@google.com>
This commit is contained in:
Jamie Madill 2020-02-07 14:17:08 -05:00 коммит произвёл Commit Bot
Родитель 788fa36035
Коммит 8257ac3051
10 изменённых файлов: 271 добавлений и 111 удалений

Просмотреть файл

@ -1093,19 +1093,54 @@ ANGLE_INLINE angle::Result ContextVk::handleDirtyTexturesImpl(const gl::Context
vk::CommandBuffer *commandBuffer,
vk::CommandGraphResource *recorder)
{
if (commandGraphEnabled())
{
ANGLE_TRY(updateActiveTextures(context));
const gl::ActiveTextureMask &activeTextures = mProgram->getState().getActiveSamplersMask();
for (size_t textureUnit : activeTextures)
{
vk::TextureUnit &unit = mActiveTextures[textureUnit];
const vk::TextureUnit &unit = mActiveTextures[textureUnit];
TextureVk *textureVk = unit.texture;
ASSERT(textureVk);
vk::ImageHelper &image = textureVk->getImage();
// The image should be flushed and ready to use at this point. There may still be
// lingering staged updates in its staging buffer for unused texture mip levels or
// layers. Therefore we can't verify it has no staged updates right here.
vk::ImageLayout textureLayout = vk::ImageLayout::AllGraphicsShadersReadOnly;
if (mProgram->getState().isCompute())
{
textureLayout = vk::ImageLayout::ComputeShaderReadOnly;
}
// Ensure the image is in read-only layout
if (commandGraphEnabled())
{
if (image.isLayoutChangeNecessary(textureLayout))
{
vk::CommandBuffer *srcLayoutChange;
VkImageAspectFlags aspectFlags = image.getAspectFlags();
ASSERT(aspectFlags != 0);
ANGLE_TRY(image.recordCommands(this, &srcLayoutChange));
image.changeLayout(aspectFlags, textureLayout, srcLayoutChange);
}
image.addReadDependency(this, recorder);
}
else
{
mRenderPassCommands.imageRead(&mResourceUseList, image.getAspectFlags(), textureLayout,
&image);
}
textureVk->onImageViewUse(&mResourceUseList);
if (unit.sampler)
{
unit.sampler->onSamplerAccess(&mResourceUseList);
}
else
{
textureVk->onSamplerUse(&mResourceUseList);
}
}
if (mProgram->hasTextures())
@ -2831,10 +2866,7 @@ angle::Result ContextVk::invalidateCurrentTextures(const gl::Context *context)
mGraphicsDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
mComputeDirtyBits.set(DIRTY_BIT_TEXTURES);
mComputeDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
}
if (!commandGraphEnabled())
{
ANGLE_TRY(updateActiveTextures(context));
}
@ -3294,46 +3326,13 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context)
{
samplerVk = nullptr;
samplerSerial = kZeroSerial;
textureVk->onSamplerUse(&mResourceUseList);
}
else
{
samplerVk = vk::GetImpl(sampler);
samplerSerial = samplerVk->getSerial();
samplerVk->onSamplerAccess(&mResourceUseList);
}
vk::ImageHelper &image = textureVk->getImage();
// The image should be flushed and ready to use at this point. There may still be
// lingering staged updates in its staging buffer for unused texture mip levels or
// layers. Therefore we can't verify it has no staged updates right here.
vk::ImageLayout textureLayout = vk::ImageLayout::AllGraphicsShadersReadOnly;
if (program->isCompute())
{
textureLayout = vk::ImageLayout::ComputeShaderReadOnly;
}
// Ensure the image is in read-only layout
if (commandGraphEnabled())
{
if (image.isLayoutChangeNecessary(textureLayout))
{
vk::CommandBuffer *srcLayoutChange;
VkImageAspectFlags aspectFlags = image.getAspectFlags();
ASSERT(aspectFlags != 0);
ANGLE_TRY(image.recordCommands(this, &srcLayoutChange));
image.changeLayout(aspectFlags, textureLayout, srcLayoutChange);
}
}
else
{
ANGLE_TRY(onImageRead(image.getAspectFlags(), textureLayout, &image));
}
textureVk->onImageViewUse(&mResourceUseList);
mActiveTextures[textureUnit].texture = textureVk;
mActiveTextures[textureUnit].sampler = samplerVk;
// Cache serials from sampler and texture, but re-use texture if no sampler bound
@ -3860,8 +3859,17 @@ angle::Result ContextVk::endRenderPass()
return mRenderPassCommands.flushToPrimary(this, &mPrimaryCommands);
}
void ContextVk::onRenderPassImageWrite(VkImageAspectFlags aspectFlags,
vk::ImageLayout imageLayout,
vk::ImageHelper *image)
{
mRenderPassCommands.imageWrite(&mResourceUseList, aspectFlags, imageLayout, image);
}
CommandBufferHelper::CommandBufferHelper()
: mGlobalMemoryBarrierSrcAccess(0),
: mImageBarrierSrcStageMask(0),
mImageBarrierDstStageMask(0),
mGlobalMemoryBarrierSrcAccess(0),
mGlobalMemoryBarrierDstAccess(0),
mGlobalMemoryBarrierStages(0)
{}
@ -3888,23 +3896,81 @@ void CommandBufferHelper::bufferWrite(vk::ResourceUseList *resourceUseList,
mGlobalMemoryBarrierStages = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
}
void CommandBufferHelper::recordBarrier(vk::PrimaryCommandBuffer *primary)
void CommandBufferHelper::imageBarrier(VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
const VkImageMemoryBarrier &imageMemoryBarrier)
{
if (mGlobalMemoryBarrierSrcAccess == 0)
ASSERT(imageMemoryBarrier.pNext == nullptr);
mImageBarrierSrcStageMask |= srcStageMask;
mImageBarrierDstStageMask |= dstStageMask;
mImageMemoryBarriers.push_back(imageMemoryBarrier);
}
void CommandBufferHelper::imageRead(vk::ResourceUseList *resourceUseList,
VkImageAspectFlags aspectFlags,
vk::ImageLayout imageLayout,
vk::ImageHelper *image)
{
image->onResourceAccess(resourceUseList);
if (image->isLayoutChangeNecessary(imageLayout))
{
image->changeLayout(aspectFlags, imageLayout, this);
}
}
void CommandBufferHelper::imageWrite(vk::ResourceUseList *resourceUseList,
VkImageAspectFlags aspectFlags,
vk::ImageLayout imageLayout,
vk::ImageHelper *image)
{
image->onResourceAccess(resourceUseList);
image->changeLayout(aspectFlags, imageLayout, this);
}
void CommandBufferHelper::executeBarriers(vk::PrimaryCommandBuffer *primary)
{
if (mImageMemoryBarriers.empty() && mGlobalMemoryBarrierSrcAccess == 0)
{
return;
}
VkPipelineStageFlags srcStages = 0;
VkPipelineStageFlags dstStages = 0;
VkMemoryBarrier memoryBarrier = {};
uint32_t memoryBarrierCount = 0;
if (mGlobalMemoryBarrierSrcAccess != 0)
{
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
memoryBarrier.srcAccessMask = mGlobalMemoryBarrierSrcAccess;
memoryBarrier.dstAccessMask = mGlobalMemoryBarrierDstAccess;
primary->memoryBarrier(mGlobalMemoryBarrierStages, mGlobalMemoryBarrierStages, &memoryBarrier);
memoryBarrierCount++;
srcStages |= mGlobalMemoryBarrierStages;
dstStages |= mGlobalMemoryBarrierStages;
mGlobalMemoryBarrierSrcAccess = 0;
mGlobalMemoryBarrierDstAccess = 0;
mGlobalMemoryBarrierStages = 0;
}
if (!mImageMemoryBarriers.empty())
{
srcStages |= mImageBarrierSrcStageMask;
dstStages |= mImageBarrierDstStageMask;
primary->pipelineBarrier(srcStages, dstStages, 0, memoryBarrierCount, &memoryBarrier, 0,
nullptr, static_cast<uint32_t>(mImageMemoryBarriers.size()),
mImageMemoryBarriers.data());
mImageMemoryBarriers.clear();
mImageBarrierSrcStageMask = 0;
mImageBarrierDstStageMask = 0;
}
else
{
primary->pipelineBarrier(srcStages, dstStages, 0, memoryBarrierCount, &memoryBarrier, 0,
nullptr, 0, nullptr);
}
}
OutsideRenderPassCommandBuffer::OutsideRenderPassCommandBuffer() = default;
@ -3916,7 +3982,7 @@ void OutsideRenderPassCommandBuffer::flushToPrimary(vk::PrimaryCommandBuffer *pr
if (empty())
return;
recordBarrier(primary);
executeBarriers(primary);
mCommandBuffer.executeCommands(primary->getHandle());
// Restart secondary buffer.
@ -3969,7 +4035,7 @@ angle::Result RenderPassCommandBuffer::flushToPrimary(ContextVk *contextVk,
if (empty())
return angle::Result::Continue;
recordBarrier(primary);
executeBarriers(primary);
// Pull a RenderPass from the cache.
RenderPassCache &renderPassCache = contextVk->getRenderPassCache();

Просмотреть файл

@ -105,14 +105,31 @@ struct CommandBufferHelper : angle::NonCopyable
VkAccessFlags writeAccessType,
vk::BufferHelper *buffer);
void imageRead(vk::ResourceUseList *resourceUseList,
VkImageAspectFlags aspectFlags,
vk::ImageLayout imageLayout,
vk::ImageHelper *image);
void imageWrite(vk::ResourceUseList *resourceUseList,
VkImageAspectFlags aspectFlags,
vk::ImageLayout imageLayout,
vk::ImageHelper *image);
void imageBarrier(VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
const VkImageMemoryBarrier &imageMemoryBarrier);
vk::CommandBuffer &getCommandBuffer() { return mCommandBuffer; }
protected:
CommandBufferHelper();
~CommandBufferHelper();
void recordBarrier(vk::PrimaryCommandBuffer *primary);
void executeBarriers(vk::PrimaryCommandBuffer *primary);
VkPipelineStageFlags mImageBarrierSrcStageMask;
VkPipelineStageFlags mImageBarrierDstStageMask;
std::vector<VkImageMemoryBarrier> mImageMemoryBarriers;
VkFlags mGlobalMemoryBarrierSrcAccess;
VkFlags mGlobalMemoryBarrierDstAccess;
VkPipelineStageFlags mGlobalMemoryBarrierStages;
@ -559,6 +576,10 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO
vk::ImageLayout imageLayout,
vk::ImageHelper *image);
void onRenderPassImageWrite(VkImageAspectFlags aspectFlags,
vk::ImageLayout imageLayout,
vk::ImageHelper *image);
angle::Result getOutsideRenderPassCommandBuffer(vk::CommandBuffer **commandBufferOut)
{
if (!mRenderPassCommands.empty())

Просмотреть файл

@ -71,8 +71,8 @@ angle::Result RenderTargetVk::onColorDraw(ContextVk *contextVk,
}
else
{
ANGLE_TRY(contextVk->onImageWrite(VK_IMAGE_ASPECT_COLOR_BIT,
vk::ImageLayout::ColorAttachment, mImage));
contextVk->onRenderPassImageWrite(VK_IMAGE_ASPECT_COLOR_BIT,
vk::ImageLayout::ColorAttachment, mImage);
}
onImageViewAccess(contextVk);
@ -99,8 +99,8 @@ angle::Result RenderTargetVk::onDepthStencilDraw(ContextVk *contextVk,
}
else
{
ANGLE_TRY(
contextVk->onImageWrite(aspectFlags, vk::ImageLayout::DepthStencilAttachment, mImage));
contextVk->onRenderPassImageWrite(aspectFlags, vk::ImageLayout::DepthStencilAttachment,
mImage);
}
onImageViewAccess(contextVk);

Просмотреть файл

@ -559,7 +559,7 @@ class SecondaryCommandBuffer final : angle::NonCopyable
void imageBarrier(VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
const VkImageMemoryBarrier *imageMemoryBarrier);
const VkImageMemoryBarrier &imageMemoryBarrier);
void memoryBarrier(VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
@ -1129,12 +1129,13 @@ ANGLE_INLINE void SecondaryCommandBuffer::fillBuffer(const Buffer &dstBuffer,
ANGLE_INLINE void SecondaryCommandBuffer::imageBarrier(
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
const VkImageMemoryBarrier *imageMemoryBarrier)
const VkImageMemoryBarrier &imageMemoryBarrier)
{
ImageBarrierParams *paramStruct = initCommand<ImageBarrierParams>(CommandID::ImageBarrier);
ASSERT(imageMemoryBarrier.pNext == nullptr);
paramStruct->srcStageMask = srcStageMask;
paramStruct->dstStageMask = dstStageMask;
paramStruct->imageMemoryBarrier = *imageMemoryBarrier;
paramStruct->imageMemoryBarrier = imageMemoryBarrier;
}
ANGLE_INLINE void SecondaryCommandBuffer::memoryBarrier(VkPipelineStageFlags srcStageMask,

Просмотреть файл

@ -2053,18 +2053,6 @@ bool ImageHelper::isLayoutChangeNecessary(ImageLayout newLayout) const
return !sameLayoutAndNoNeedForBarrier;
}
void ImageHelper::changeLayout(VkImageAspectFlags aspectMask,
ImageLayout newLayout,
CommandBuffer *commandBuffer)
{
if (!isLayoutChangeNecessary(newLayout))
{
return;
}
forceChangeLayoutAndQueue(aspectMask, newLayout, mCurrentQueueFamilyIndex, commandBuffer);
}
void ImageHelper::changeLayoutAndQueue(VkImageAspectFlags aspectMask,
ImageLayout newLayout,
uint32_t newQueueFamilyIndex,
@ -2094,10 +2082,12 @@ void ImageHelper::setBaseAndMaxLevels(uint32_t baseLevel, uint32_t maxLevel)
mMaxLevel = maxLevel;
}
// Generalized to accept both "primary" and "secondary" command buffers.
template <typename CommandBufferT>
void ImageHelper::forceChangeLayoutAndQueue(VkImageAspectFlags aspectMask,
ImageLayout newLayout,
uint32_t newQueueFamilyIndex,
CommandBuffer *commandBuffer)
CommandBufferT *commandBuffer)
{
const ImageMemoryBarrierData &transitionFrom = kImageMemoryBarrierData[mCurrentLayout];
const ImageMemoryBarrierData &transitionTo = kImageMemoryBarrierData[newLayout];
@ -2112,7 +2102,7 @@ void ImageHelper::forceChangeLayoutAndQueue(VkImageAspectFlags aspectMask,
imageMemoryBarrier.dstQueueFamilyIndex = newQueueFamilyIndex;
imageMemoryBarrier.image = mImage.getHandle();
// TODO(jmadill): Is this needed for mipped/layer images?
// Transition the whole resource.
imageMemoryBarrier.subresourceRange.aspectMask = aspectMask;
imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
imageMemoryBarrier.subresourceRange.levelCount = mLevelCount;
@ -2120,11 +2110,17 @@ void ImageHelper::forceChangeLayoutAndQueue(VkImageAspectFlags aspectMask,
imageMemoryBarrier.subresourceRange.layerCount = mLayerCount;
commandBuffer->imageBarrier(transitionFrom.srcStageMask, transitionTo.dstStageMask,
&imageMemoryBarrier);
imageMemoryBarrier);
mCurrentLayout = newLayout;
mCurrentQueueFamilyIndex = newQueueFamilyIndex;
}
// Explicitly instantiate forceChangeLayoutAndQueue with CommandBufferHelper.
template void ImageHelper::forceChangeLayoutAndQueue(VkImageAspectFlags aspectMask,
ImageLayout newLayout,
uint32_t newQueueFamilyIndex,
CommandBufferHelper *commandBuffer);
void ImageHelper::clearColor(const VkClearColorValue &color,
uint32_t baseMipLevel,
uint32_t levelCount,
@ -2278,7 +2274,7 @@ angle::Result ImageHelper::generateMipmapsWithBlit(ContextVk *contextVk, GLuint
// We can do it for all layers at once.
commandBuffer->imageBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
&barrier);
barrier);
VkImageBlit blit = {};
blit.srcOffsets[0] = {0, 0, 0};
blit.srcOffsets[1] = {mipWidth, mipHeight, 1};
@ -2313,7 +2309,7 @@ angle::Result ImageHelper::generateMipmapsWithBlit(ContextVk *contextVk, GLuint
// We can do it for all layers at once.
commandBuffer->imageBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
&barrier);
barrier);
// This is just changing the internal state of the image helper so that the next call
// to changeLayout will use this layout as the "oldLayout" argument.
mCurrentLayout = ImageLayout::TransferSrc;

Просмотреть файл

@ -892,9 +892,18 @@ class ImageHelper final : public CommandGraphResource
// purpose of performing a transition (which may then not be issued).
bool isLayoutChangeNecessary(ImageLayout newLayout) const;
template <typename CommandBufferT>
void changeLayout(VkImageAspectFlags aspectMask,
ImageLayout newLayout,
CommandBuffer *commandBuffer);
CommandBufferT *commandBuffer)
{
if (!isLayoutChangeNecessary(newLayout))
{
return;
}
forceChangeLayoutAndQueue(aspectMask, newLayout, mCurrentQueueFamilyIndex, commandBuffer);
}
bool isQueueChangeNeccesary(uint32_t newQueueFamilyIndex) const
{
@ -962,10 +971,12 @@ class ImageHelper final : public CommandGraphResource
GLuint *inputSkipBytes);
private:
// Generalized to accept both "primary" and "secondary" command buffers.
template <typename CommandBufferT>
void forceChangeLayoutAndQueue(VkImageAspectFlags aspectMask,
ImageLayout newLayout,
uint32_t newQueueFamilyIndex,
CommandBuffer *commandBuffer);
CommandBufferT *commandBuffer);
void stageSubresourceClear(const gl::ImageIndex &index,
const angle::Format &format,

Просмотреть файл

@ -310,7 +310,7 @@ class CommandBuffer : public WrappedObject<CommandBuffer, VkCommandBuffer>
void imageBarrier(VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
const VkImageMemoryBarrier *imageMemoryBarrier);
const VkImageMemoryBarrier &imageMemoryBarrier);
void memoryBarrier(VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
@ -702,11 +702,11 @@ ANGLE_INLINE void CommandBuffer::bufferBarrier(VkPipelineStageFlags srcStageMask
ANGLE_INLINE void CommandBuffer::imageBarrier(VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
const VkImageMemoryBarrier *imageMemoryBarrier)
const VkImageMemoryBarrier &imageMemoryBarrier)
{
ASSERT(valid());
vkCmdPipelineBarrier(mHandle, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1,
imageMemoryBarrier);
&imageMemoryBarrier);
}
ANGLE_INLINE void CommandBuffer::destroy(VkDevice device)

Просмотреть файл

@ -15,26 +15,6 @@ using namespace angle;
namespace
{
Vector4 RandomVec4(int seed, float minValue, float maxValue)
{
RNG rng(seed);
srand(seed);
return Vector4(
rng.randomFloatBetween(minValue, maxValue), rng.randomFloatBetween(minValue, maxValue),
rng.randomFloatBetween(minValue, maxValue), rng.randomFloatBetween(minValue, maxValue));
}
GLColor Vec4ToColor(const Vector4 &vec)
{
GLColor color;
color.R = static_cast<uint8_t>(vec.x() * 255.0f);
color.G = static_cast<uint8_t>(vec.y() * 255.0f);
color.B = static_cast<uint8_t>(vec.z() * 255.0f);
color.A = static_cast<uint8_t>(vec.w() * 255.0f);
return color;
}
class ClearTestBase : public ANGLETest
{
protected:
@ -1216,7 +1196,7 @@ TEST_P(ClearTestES3, RepeatedClear)
{
int seed = cellX + cellY * numRowsCols;
const Vector4 color = RandomVec4(seed, fmtValueMin, fmtValueMax);
GLColor expectedColor = Vec4ToColor(color);
GLColor expectedColor(color);
int testN = cellX * cellSize + cellY * backFBOSize * cellSize + backFBOSize + 1;
GLColor actualColor = pixelData[testN];

Просмотреть файл

@ -10,6 +10,7 @@
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
#include "util/random_utils.h"
using namespace angle;
@ -2596,6 +2597,82 @@ TEST_P(SimpleStateChangeTest, RedefineFramebufferTexture)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green) << "second draw should be green";
}
// Trips a bug in the Vulkan back-end where a Texture wouldn't transition correctly.
TEST_P(SimpleStateChangeTest, DrawAndClearTextureRepeatedly)
{
// Fails on 431.02 driver. http://anglebug.com/3748
ANGLE_SKIP_TEST_IF(IsWindows() && IsNVIDIA() && IsVulkan());
// Fails on AMD OpenGL Windows. This configuration isn't maintained.
ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsOpenGL());
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::red);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glUseProgram(program);
GLint uniLoc = glGetUniformLocation(program, essl1_shaders::Texture2DUniform());
ASSERT_NE(-1, uniLoc);
glUniform1i(uniLoc, 0);
const int numRowsCols = 2;
const int cellSize = getWindowWidth() / 2;
for (int cellY = 0; cellY < numRowsCols; cellY++)
{
for (int cellX = 0; cellX < numRowsCols; cellX++)
{
int seed = cellX + cellY * numRowsCols;
const Vector4 color = RandomVec4(seed, 0.0f, 1.0f);
// Set the texture to a constant color using glClear and a user FBO.
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glClearColor(color[0], color[1], color[2], color[3]);
glClear(GL_COLOR_BUFFER_BIT);
// Draw a small colored quad to the default FBO using the viewport.
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(cellX * cellSize, cellY * cellSize, cellSize, cellSize);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
}
}
// Verify the colored quads were drawn correctly despite no flushing.
std::vector<GLColor> pixelData(getWindowWidth() * getWindowHeight());
glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
pixelData.data());
ASSERT_GL_NO_ERROR();
for (int cellY = 0; cellY < numRowsCols; cellY++)
{
for (int cellX = 0; cellX < numRowsCols; cellX++)
{
int seed = cellX + cellY * numRowsCols;
const Vector4 color = RandomVec4(seed, 0.0f, 1.0f);
GLColor expectedColor(color);
int testN =
cellX * cellSize + cellY * getWindowWidth() * cellSize + getWindowWidth() + 1;
GLColor actualColor = pixelData[testN];
EXPECT_COLOR_NEAR(expectedColor, actualColor, 1);
}
}
}
// Validates disabling cull face really disables it.
TEST_P(SimpleStateChangeTest, EnableAndDisableCullFace)
{

Просмотреть файл

@ -10,10 +10,10 @@
#ifndef UTIL_RANDOM_UTILS_H
#define UTIL_RANDOM_UTILS_H
// TODO(jmadill): Rework this if Chromium decides to ban <random>
#include <random>
#include <vector>
#include "common/vector_utils.h"
#include "util/util_export.h"
namespace angle
@ -70,6 +70,14 @@ inline void FillVectorWithRandomUBytes(std::vector<uint8_t> *data)
FillVectorWithRandomUBytes(&rng, data);
}
inline Vector4 RandomVec4(int seed, float minValue, float maxValue)
{
RNG rng(seed);
srand(seed);
return Vector4(
rng.randomFloatBetween(minValue, maxValue), rng.randomFloatBetween(minValue, maxValue),
rng.randomFloatBetween(minValue, maxValue), rng.randomFloatBetween(minValue, maxValue));
}
} // namespace angle
#endif // UTIL_RANDOM_UTILS_H