зеркало из https://github.com/AvaloniaUI/angle.git
Vulkan: Enable VK_IMAGE_USAGE_STORAGE_BIT when it is needed
VK_IMAGE_USAGE_STORAGE_BIT is always enabled for vkImage, this increases memory bandwidth in some platforms. This CL changes the behavior to enable VK_IMAGE_USAGE_STORAGE_BIT when necessary. Bug: angleproject:3904 Test: angle_end2end_tests Test: angle_deqp_gles2_tests Change-Id: I8ffd37efa8d99d04328fa6232de0755be3273d9e Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1857799 Commit-Queue: Sunny Sun <sunny.sun@arm.com> Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Родитель
67527cb452
Коммит
df41552841
|
@ -165,3 +165,4 @@ Samsung Electronics, Inc.
|
|||
Arm Ltd.
|
||||
Fei Yang
|
||||
Xinyi He
|
||||
Sunny Sun
|
||||
|
|
|
@ -486,6 +486,7 @@ void Context::initialize()
|
|||
mDrawDirtyObjects.set(State::DIRTY_OBJECT_TEXTURES);
|
||||
mDrawDirtyObjects.set(State::DIRTY_OBJECT_PROGRAM);
|
||||
mDrawDirtyObjects.set(State::DIRTY_OBJECT_SAMPLERS);
|
||||
mDrawDirtyObjects.set(State::DIRTY_OBJECT_IMAGES);
|
||||
|
||||
mPathOperationDirtyObjects.set(State::DIRTY_OBJECT_DRAW_FRAMEBUFFER);
|
||||
mPathOperationDirtyObjects.set(State::DIRTY_OBJECT_VERTEX_ARRAY);
|
||||
|
|
|
@ -2881,6 +2881,10 @@ void State::setImageUnit(const Context *context,
|
|||
mImageUnits[unit].format = format;
|
||||
mDirtyBits.set(DIRTY_BIT_IMAGE_BINDINGS);
|
||||
|
||||
if (texture)
|
||||
{
|
||||
texture->onBindImageTexture();
|
||||
}
|
||||
onImageStateChange(context, unit);
|
||||
}
|
||||
|
||||
|
|
|
@ -99,6 +99,7 @@ TextureState::TextureState(TextureType type)
|
|||
mBaseLevel(0),
|
||||
mMaxLevel(1000),
|
||||
mDepthStencilTextureMode(GL_DEPTH_COMPONENT),
|
||||
mBoundAsImageTexture(false),
|
||||
mImmutableFormat(false),
|
||||
mImmutableLevels(0),
|
||||
mUsage(GL_NONE),
|
||||
|
@ -1908,4 +1909,14 @@ angle::Result Texture::getTexImage(const Context *context,
|
|||
return mTexture->getTexImage(context, packState, packBuffer, target, level, format, type,
|
||||
pixels);
|
||||
}
|
||||
|
||||
void Texture::onBindImageTexture()
|
||||
{
|
||||
if (!mState.mBoundAsImageTexture)
|
||||
{
|
||||
mDirtyBits.set(DIRTY_BIT_BOUND_AS_IMAGE);
|
||||
mState.mBoundAsImageTexture = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
|
|
|
@ -190,6 +190,7 @@ class TextureState final : private angle::NonCopyable
|
|||
|
||||
GLenum mDepthStencilTextureMode;
|
||||
|
||||
bool mBoundAsImageTexture;
|
||||
bool mImmutableFormat;
|
||||
GLuint mImmutableLevels;
|
||||
|
||||
|
@ -412,6 +413,7 @@ class Texture final : public RefCountObject<TextureID>,
|
|||
angle::Result setEGLImageTarget(Context *context, TextureType type, egl::Image *imageTarget);
|
||||
|
||||
angle::Result generateMipmap(Context *context);
|
||||
void onBindImageTexture();
|
||||
|
||||
egl::Surface *getBoundSurface() const;
|
||||
egl::Stream *getBoundStream() const;
|
||||
|
@ -493,6 +495,9 @@ class Texture final : public RefCountObject<TextureID>,
|
|||
DIRTY_BIT_MAX_LEVEL,
|
||||
DIRTY_BIT_DEPTH_STENCIL_TEXTURE_MODE,
|
||||
|
||||
// Image state
|
||||
DIRTY_BIT_BOUND_AS_IMAGE,
|
||||
|
||||
// Misc
|
||||
DIRTY_BIT_LABEL,
|
||||
DIRTY_BIT_USAGE,
|
||||
|
|
|
@ -1462,6 +1462,9 @@ angle::Result TextureGL::syncState(const gl::Context *context,
|
|||
// This special dirty bit is used to signal the front-end that the implementation
|
||||
// has local dirty bits. The real dirty bits are in mLocalDirty bits.
|
||||
break;
|
||||
case gl::Texture::DIRTY_BIT_BOUND_AS_IMAGE:
|
||||
// Only used for Vulkan.
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
|
|
@ -107,7 +107,8 @@ TextureVk::TextureVk(const gl::TextureState &state, RendererVk *renderer)
|
|||
mImageLayerOffset(0),
|
||||
mImageLevelOffset(0),
|
||||
mImage(nullptr),
|
||||
mStagingBufferInitialSize(vk::kStagingBufferSize)
|
||||
mStagingBufferInitialSize(vk::kStagingBufferSize),
|
||||
mImageUsageFlags(0)
|
||||
{}
|
||||
|
||||
TextureVk::~TextureVk() = default;
|
||||
|
@ -727,10 +728,6 @@ angle::Result TextureVk::setStorage(const gl::Context *context,
|
|||
{
|
||||
releaseImage(contextVk);
|
||||
}
|
||||
|
||||
gl::Format glFormat(internalFormat);
|
||||
ANGLE_TRY(
|
||||
initImage(contextVk, format, glFormat.info->sized, size, static_cast<uint32_t>(levels)));
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
|
@ -867,6 +864,21 @@ angle::Result TextureVk::ensureImageAllocated(ContextVk *contextVk, const vk::Fo
|
|||
updateImageHelper(contextVk, format);
|
||||
}
|
||||
|
||||
mImageUsageFlags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
|
||||
// If the image has depth/stencil support, add those as possible usage.
|
||||
if (contextVk->getRenderer()->hasImageFormatFeatureBits(
|
||||
format.vkImageFormat, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT))
|
||||
{
|
||||
mImageUsageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
}
|
||||
else if (contextVk->getRenderer()->hasImageFormatFeatureBits(
|
||||
format.vkImageFormat, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
|
||||
{
|
||||
mImageUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
}
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
|
@ -1052,15 +1064,24 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context)
|
|||
|
||||
// Redefine the images with mipmaps.
|
||||
// Copy image to the staging buffer and stage an update to the new one.
|
||||
vk::BufferHelper *stagingBuffer = nullptr;
|
||||
ANGLE_TRY(copyImageDataToStagingBuffer(contextVk, baseLevelDesc, false,
|
||||
getNativeImageLayer(0), 0, mImage->getBaseLevel()));
|
||||
getNativeImageLayer(0), 0, mImage->getBaseLevel(),
|
||||
&stagingBuffer));
|
||||
|
||||
onStagingBufferChange();
|
||||
|
||||
// Release the origin image and recreate it with new mipmap counts.
|
||||
releaseImage(contextVk);
|
||||
|
||||
ANGLE_TRY(ensureImageInitialized(contextVk, ImageMipLevels::FullMipChain));
|
||||
// Set up read dependency, we are now reading from this buffer to the new image.
|
||||
if (stagingBuffer)
|
||||
{
|
||||
stagingBuffer->onRead(contextVk, mImage, VK_ACCESS_TRANSFER_READ_BIT);
|
||||
// Different parts of the buffer might be read from or write to.
|
||||
stagingBuffer->onSelfReadWrite(contextVk, VK_ACCESS_TRANSFER_READ_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
}
|
||||
}
|
||||
// Check if the image supports blit. If it does, we can do the mipmap generation on the gpu
|
||||
// only.
|
||||
|
@ -1082,7 +1103,8 @@ angle::Result TextureVk::copyImageDataToStagingBuffer(ContextVk *contextVk,
|
|||
bool ignoreLayerCount,
|
||||
uint32_t currentLayer,
|
||||
uint32_t sourceMipLevel,
|
||||
uint32_t stagingDstMipLevel)
|
||||
uint32_t stagingDstMipLevel,
|
||||
vk::BufferHelper **stagingBuffer)
|
||||
{
|
||||
const gl::Extents &baseLevelExtents = desc.size;
|
||||
const gl::InternalFormat &baseLevelFormat = *desc.format.info;
|
||||
|
@ -1101,18 +1123,21 @@ angle::Result TextureVk::copyImageDataToStagingBuffer(ContextVk *contextVk,
|
|||
}
|
||||
|
||||
// Copy from the base level image to the staging buffer
|
||||
vk::BufferHelper *stagingBuffer = nullptr;
|
||||
VkDeviceSize stagingBufferOffset = 0;
|
||||
ANGLE_TRY(mImage->copyImageDataToBuffer(contextVk, sourceMipLevel, layerCount, currentLayer,
|
||||
area, &stagingBuffer, &stagingBufferOffset, nullptr));
|
||||
area, stagingBuffer, &stagingBufferOffset, nullptr));
|
||||
|
||||
// Stage an update to the new image
|
||||
size_t bufferSize = updatedExtents.width * updatedExtents.height * updatedExtents.depth *
|
||||
baseLevelFormat.pixelBytes;
|
||||
|
||||
ANGLE_TRY(mImage->stageSubresourceUpdateFromBuffer(contextVk, bufferSize, stagingDstMipLevel,
|
||||
currentLayer, layerCount, updatedExtents,
|
||||
offset, stagingBuffer, stagingBufferOffset));
|
||||
ASSERT(*stagingBuffer);
|
||||
ANGLE_TRY(mImage->stageSubresourceUpdateFromBuffer(
|
||||
contextVk, bufferSize, stagingDstMipLevel, currentLayer, layerCount, updatedExtents, offset,
|
||||
*stagingBuffer, stagingBufferOffset));
|
||||
|
||||
// Set up write dependency, we are writing to this buffer from the old image.
|
||||
(*stagingBuffer)->onWrite(contextVk, mImage, 0, VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
@ -1122,7 +1147,10 @@ angle::Result TextureVk::setBaseLevel(const gl::Context *context, GLuint baseLev
|
|||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
angle::Result TextureVk::changeLevels(ContextVk *contextVk, GLuint baseLevel, GLuint maxLevel)
|
||||
angle::Result TextureVk::updateBaseMaxLevels(ContextVk *contextVk,
|
||||
GLuint baseLevel,
|
||||
GLuint maxLevel,
|
||||
vk::BufferHelper **stagingBuffer)
|
||||
{
|
||||
if (!mImage)
|
||||
{
|
||||
|
@ -1151,15 +1179,27 @@ angle::Result TextureVk::changeLevels(ContextVk *contextVk, GLuint baseLevel, GL
|
|||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
// If we get here, we already have a valid image and it needs to be recreated
|
||||
// to reflect new base or max levels.
|
||||
return changeLevels(contextVk, previousBaseLevel, baseLevel, maxLevel, stagingBuffer);
|
||||
}
|
||||
|
||||
angle::Result TextureVk::changeLevels(ContextVk *contextVk,
|
||||
GLuint previousBaseLevel,
|
||||
GLuint baseLevel,
|
||||
GLuint maxLevel,
|
||||
vk::BufferHelper **stagingBuffer)
|
||||
{
|
||||
// Recreate the image to reflect new base or max levels.
|
||||
// First, flush any pending updates so we have good data in the existing vkImage
|
||||
vk::CommandBuffer *commandBuffer = nullptr;
|
||||
ANGLE_TRY(mImage->recordCommands(contextVk, &commandBuffer));
|
||||
ANGLE_TRY(mImage->flushStagedUpdates(contextVk, getNativeImageLevel(0), mImage->getLevelCount(),
|
||||
getNativeImageLayer(0), mImage->getLayerCount(),
|
||||
commandBuffer));
|
||||
if (mImage->valid() && mImage->hasStagedUpdates())
|
||||
{
|
||||
vk::CommandBuffer *commandBuffer = nullptr;
|
||||
ANGLE_TRY(mImage->recordCommands(contextVk, &commandBuffer));
|
||||
ANGLE_TRY(mImage->flushStagedUpdates(contextVk, getNativeImageLevel(0),
|
||||
mImage->getLevelCount(), getNativeImageLayer(0),
|
||||
mImage->getLayerCount(), commandBuffer));
|
||||
}
|
||||
|
||||
bool baseLevelChanged = baseLevel != previousBaseLevel;
|
||||
|
||||
// After flushing, track the new levels (they are used in the flush, hence the wait)
|
||||
mImage->setBaseAndMaxLevels(baseLevel, maxLevel);
|
||||
|
@ -1198,14 +1238,13 @@ angle::Result TextureVk::changeLevels(ContextVk *contextVk, GLuint baseLevel, GL
|
|||
uint32_t srcLevelVK = baseLevelChanged ? level - previousBaseLevel : level;
|
||||
ASSERT(srcLevelVK <= mImage->getLevelCount());
|
||||
|
||||
ANGLE_TRY(
|
||||
copyImageDataToStagingBuffer(contextVk, desc, true, layer, srcLevelVK, level));
|
||||
ANGLE_TRY(copyImageDataToStagingBuffer(contextVk, desc, true, layer, srcLevelVK, level,
|
||||
stagingBuffer));
|
||||
}
|
||||
}
|
||||
|
||||
// Inform the front end that we've updated the staging buffer
|
||||
onStagingBufferChange();
|
||||
|
||||
// Now that we've staged all the updates, release the current image so that it will be
|
||||
// recreated with the correct number of mip levels, base level, and max level.
|
||||
releaseImage(contextVk);
|
||||
|
@ -1326,17 +1365,39 @@ angle::Result TextureVk::syncState(const gl::Context *context,
|
|||
{
|
||||
ContextVk *contextVk = vk::GetImpl(context);
|
||||
|
||||
vk::BufferHelper *stagingBuffer = nullptr;
|
||||
// Create a new image if the storage state is enabled for the first time.
|
||||
if (dirtyBits.test(gl::Texture::DIRTY_BIT_BOUND_AS_IMAGE))
|
||||
{
|
||||
// Recreate the image to include storage bit if needed.
|
||||
if (!(mImageUsageFlags & VK_IMAGE_USAGE_STORAGE_BIT))
|
||||
{
|
||||
mImageUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
ANGLE_TRY(changeLevels(contextVk, mImage->getBaseLevel(),
|
||||
mState.getEffectiveBaseLevel(), mState.getEffectiveMaxLevel(),
|
||||
&stagingBuffer));
|
||||
}
|
||||
}
|
||||
|
||||
// Set base and max level before initializing the image
|
||||
if (dirtyBits.test(gl::Texture::DIRTY_BIT_MAX_LEVEL) ||
|
||||
dirtyBits.test(gl::Texture::DIRTY_BIT_BASE_LEVEL))
|
||||
{
|
||||
ANGLE_TRY(
|
||||
changeLevels(contextVk, mState.getEffectiveBaseLevel(), mState.getEffectiveMaxLevel()));
|
||||
ANGLE_TRY(updateBaseMaxLevels(contextVk, mState.getEffectiveBaseLevel(),
|
||||
mState.getEffectiveMaxLevel(), &stagingBuffer));
|
||||
}
|
||||
|
||||
// Initialize the image storage and flush the pixel buffer.
|
||||
ANGLE_TRY(ensureImageInitialized(contextVk, ImageMipLevels::EnabledLevels));
|
||||
|
||||
if (stagingBuffer)
|
||||
{
|
||||
stagingBuffer->onRead(contextVk, mImage, VK_ACCESS_TRANSFER_READ_BIT);
|
||||
// Different parts of the buffer might be read from or write to.
|
||||
stagingBuffer->onSelfReadWrite(contextVk, VK_ACCESS_TRANSFER_READ_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
}
|
||||
|
||||
if (dirtyBits.none() && mSampler.valid())
|
||||
{
|
||||
return angle::Result::Continue;
|
||||
|
@ -1520,34 +1581,11 @@ angle::Result TextureVk::initImage(ContextVk *contextVk,
|
|||
{
|
||||
RendererVk *renderer = contextVk->getRenderer();
|
||||
|
||||
VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
|
||||
// If the image has depth/stencil support, add those as possible usage.
|
||||
if (renderer->hasImageFormatFeatureBits(format.vkImageFormat,
|
||||
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT))
|
||||
{
|
||||
imageUsageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
}
|
||||
else if (renderer->hasImageFormatFeatureBits(format.vkImageFormat,
|
||||
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
|
||||
{
|
||||
imageUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
}
|
||||
|
||||
// If the image has storage support, add it for ES3.1 image support.
|
||||
if (renderer->hasImageFormatFeatureBits(format.vkImageFormat,
|
||||
VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT))
|
||||
{
|
||||
imageUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
}
|
||||
|
||||
VkExtent3D vkExtent;
|
||||
uint32_t layerCount;
|
||||
gl_vk::GetExtentsAndLayerCount(mState.getType(), extents, &vkExtent, &layerCount);
|
||||
|
||||
ANGLE_TRY(mImage->init(contextVk, mState.getType(), vkExtent, format, 1, imageUsageFlags,
|
||||
ANGLE_TRY(mImage->init(contextVk, mState.getType(), vkExtent, format, 1, mImageUsageFlags,
|
||||
mState.getEffectiveBaseLevel(), mState.getEffectiveMaxLevel(),
|
||||
levelCount, layerCount));
|
||||
|
||||
|
|
|
@ -322,7 +322,8 @@ class TextureVk : public TextureImpl
|
|||
bool ignoreLayerCount,
|
||||
uint32_t currentLayer,
|
||||
uint32_t sourceLevel,
|
||||
uint32_t stagingDstMipLevel);
|
||||
uint32_t stagingDstMipLevel,
|
||||
vk::BufferHelper **stagingBuffer);
|
||||
angle::Result initImageViews(ContextVk *contextVk,
|
||||
const vk::Format &format,
|
||||
const bool sized,
|
||||
|
@ -341,9 +342,20 @@ class TextureVk : public TextureImpl
|
|||
|
||||
void onStagingBufferChange() { onStateChange(angle::SubjectMessage::SubjectChanged); }
|
||||
|
||||
angle::Result changeLevels(ContextVk *contextVk, GLuint baseLevel, GLuint maxLevel);
|
||||
const gl::InternalFormat &getImplementationSizedFormat(const gl::Context *context) const;
|
||||
const vk::Format &getBaseLevelFormat(RendererVk *renderer) const;
|
||||
// Re-create the image.
|
||||
angle::Result changeLevels(ContextVk *contextVk,
|
||||
GLuint previousBaseLevel,
|
||||
GLuint baseLevel,
|
||||
GLuint maxLevel,
|
||||
vk::BufferHelper **stagingBuffer);
|
||||
|
||||
// Update base and max levels, and re-create image if needed.
|
||||
angle::Result updateBaseMaxLevels(ContextVk *contextVk,
|
||||
GLuint baseLevel,
|
||||
GLuint maxLevel,
|
||||
vk::BufferHelper **stagingBuffer);
|
||||
|
||||
bool mOwnsImage;
|
||||
|
||||
|
@ -380,6 +392,9 @@ class TextureVk : public TextureImpl
|
|||
|
||||
// Overridden in some tests.
|
||||
size_t mStagingBufferInitialSize;
|
||||
|
||||
// The created vkImage usage flag.
|
||||
VkImageUsageFlags mImageUsageFlags;
|
||||
};
|
||||
|
||||
} // namespace rx
|
||||
|
|
|
@ -574,6 +574,10 @@
|
|||
// Fixed in later driver versions.
|
||||
2727 VULKAN ANDROID : dEQP-GLES3.functional.shaders.builtin_variable.pointcoord = FAIL
|
||||
|
||||
// New or broken formats in ES 3.0, may be it relates to VK_IMAGE_USAGE_STORAGE_BIT
|
||||
3816 VULKAN PIXEL2ORXL : dEQP-GLES3.functional.texture.specification.texstorage3d.format.rgba16* = FAIL
|
||||
3816 VULKAN PIXEL2ORXL : dEQP-GLES3.functional.texture.specification.texstorage3d.format.rgb16* = FAIL
|
||||
3816 VULKAN PIXEL2ORXL : dEQP-GLES3.functional.texture.specification.texstorage3d.format.rg32* = FAIL
|
||||
|
||||
// Fails only with SwiftShader:
|
||||
|
||||
|
|
|
@ -3508,6 +3508,117 @@ void main()
|
|||
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
|
||||
}
|
||||
|
||||
// Test that render pipeline and compute pipeline access to the same texture.
|
||||
// Steps:
|
||||
// 1. Clear the texture and DrawArrays.
|
||||
// 2. DispatchCompute to set the image's first pixel to a specific color.
|
||||
// 3. DrawArrays and check data.
|
||||
TEST_P(ComputeShaderTest, DrawDispatchDrawPreserve)
|
||||
{
|
||||
const char kCSSource[] = R"(#version 310 es
|
||||
layout(local_size_x=1, local_size_y=1) in;
|
||||
layout(rgba8, binding = 0) writeonly uniform highp image2D image;
|
||||
void main()
|
||||
{
|
||||
imageStore(image, ivec2(0, 0), vec4(0.0, 0.0, 1.0, 1.0));
|
||||
})";
|
||||
|
||||
const char kVSSource[] = R"(#version 310 es
|
||||
layout (location = 0) in vec2 pos;
|
||||
in vec4 inTex;
|
||||
out vec4 texCoord;
|
||||
void main(void) {
|
||||
texCoord = inTex;
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
})";
|
||||
|
||||
const char kFSSource[] = R"(#version 310 es
|
||||
precision highp float;
|
||||
uniform sampler2D tex;
|
||||
in vec4 texCoord;
|
||||
out vec4 fragColor;
|
||||
void main(void) {
|
||||
fragColor = texture(tex, texCoord.xy);
|
||||
})";
|
||||
GLuint aPosLoc = 0;
|
||||
ANGLE_GL_PROGRAM(program, kVSSource, kFSSource);
|
||||
glBindAttribLocation(program, aPosLoc, "pos");
|
||||
|
||||
unsigned char *data = new unsigned char[4 * getWindowWidth() * getWindowHeight()];
|
||||
for (int i = 0; i < getWindowWidth() * getWindowHeight(); i++)
|
||||
{
|
||||
data[i * 4] = 0xff;
|
||||
data[i * 4 + 1] = 0;
|
||||
data[i * 4 + 2] = 0;
|
||||
data[i * 4 + 3] = 0xff;
|
||||
}
|
||||
GLTexture texture;
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 2, GL_RGBA8, getWindowWidth(), getWindowHeight());
|
||||
// Clear the texture level 0 to Red.
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, data);
|
||||
for (int i = 0; i < getWindowWidth() * getWindowHeight(); i++)
|
||||
{
|
||||
data[i * 4] = 0;
|
||||
data[i * 4 + 1] = 0xff;
|
||||
data[i * 4 + 2] = 0;
|
||||
data[i * 4 + 3] = 0xff;
|
||||
}
|
||||
// Clear the texture level 1 to Green.
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, getWindowWidth() / 2, getWindowHeight() / 2, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, data);
|
||||
delete[] data;
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glUseProgram(program);
|
||||
GLfloat vertices[] = {-1, -1, 1, -1, -1, 1, 1, 1};
|
||||
GLfloat texCoords[] = {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f};
|
||||
GLint pos = glGetAttribLocation(program, "pos");
|
||||
glEnableVertexAttribArray(pos);
|
||||
glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 0, vertices);
|
||||
GLint posTex = glGetAttribLocation(program, "inTex");
|
||||
glEnableVertexAttribArray(posTex);
|
||||
glVertexAttribPointer(posTex, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
|
||||
|
||||
// Draw with level 0, the whole frame buffer should be Red.
|
||||
glViewport(0, 0, getWindowWidth(), getWindowHeight());
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
EXPECT_GL_NO_ERROR();
|
||||
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
|
||||
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
|
||||
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red);
|
||||
// Draw with level 1, the whole frame buffer should be Green.
|
||||
glViewport(0, 0, getWindowWidth() / 2, getWindowHeight() / 2);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
|
||||
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
|
||||
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2 - 1, getWindowHeight() / 2 - 1, GLColor::green);
|
||||
|
||||
// Clear the texture level 0's (0, 0) position to Blue.
|
||||
glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);
|
||||
ANGLE_GL_COMPUTE_PROGRAM(csProgram, kCSSource);
|
||||
glUseProgram(csProgram);
|
||||
glDispatchCompute(1, 1, 1);
|
||||
glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT);
|
||||
EXPECT_GL_NO_ERROR();
|
||||
glFinish();
|
||||
|
||||
glUseProgram(program);
|
||||
// Draw with level 0, the first position should be Blue.
|
||||
glViewport(0, 0, getWindowWidth(), getWindowHeight());
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
|
||||
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
|
||||
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red);
|
||||
// Draw with level 1, the whole frame buffer should be Green.
|
||||
glViewport(0, 0, getWindowWidth() / 2, getWindowHeight() / 2);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
|
||||
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
|
||||
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2 - 1, getWindowHeight() / 2 - 1, GLColor::green);
|
||||
}
|
||||
|
||||
ANGLE_INSTANTIATE_TEST(ComputeShaderTest,
|
||||
ES31_OPENGL(),
|
||||
ES31_OPENGLES(),
|
||||
|
|
Загрузка…
Ссылка в новой задаче