Commit bdecaf33 by Le Hoang Quyen Committed by Commit Bot

Metal: Implement PBO.

Bug: angleproject:2634 Change-Id: I77f085227298bf46361825d1886e04830dc9987a Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2336558 Commit-Queue: Le Hoang Quyen <le.hoang.q@gmail.com> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 7b7e52fa
......@@ -28,7 +28,7 @@ namespace rx
// Conversion buffers hold translated index and vertex data.
struct ConversionBufferMtl
{
ConversionBufferMtl(const gl::Context *context, size_t initialSize, size_t alignment);
ConversionBufferMtl(ContextMtl *context, size_t initialSize, size_t alignment);
~ConversionBufferMtl();
// One state value determines if we need to re-stream vertex data.
......@@ -42,13 +42,24 @@ struct ConversionBufferMtl
size_t convertedOffset;
};
struct VertexConversionBufferMtl : public ConversionBufferMtl
{
VertexConversionBufferMtl(ContextMtl *context,
angle::FormatID formatIDIn,
GLuint strideIn,
size_t offsetIn);
// The conversion is identified by the triple of {format, stride, offset}.
angle::FormatID formatID;
GLuint stride;
size_t offset;
};
struct IndexConversionBufferMtl : public ConversionBufferMtl
{
IndexConversionBufferMtl(const gl::Context *context,
gl::DrawElementsType type,
size_t offsetIn);
IndexConversionBufferMtl(ContextMtl *context, gl::DrawElementsType elemType, size_t offsetIn);
const gl::DrawElementsType type;
const gl::DrawElementsType elemType;
const size_t offset;
};
......@@ -108,25 +119,32 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl
bool primitiveRestartEnabled,
gl::IndexRange *outRange) override;
angle::Result getFirstLastIndices(gl::DrawElementsType type,
void onDataChanged() override;
angle::Result getFirstLastIndices(ContextMtl *contextMtl,
gl::DrawElementsType type,
size_t offset,
size_t count,
std::pair<uint32_t, uint32_t> *outIndices) const;
std::pair<uint32_t, uint32_t> *outIndices);
const uint8_t *getClientShadowCopyData(const gl::Context *context);
const uint8_t *getClientShadowCopyData(ContextMtl *contextMtl);
ConversionBufferMtl *getVertexConversionBuffer(const gl::Context *context,
ConversionBufferMtl *getVertexConversionBuffer(ContextMtl *context,
angle::FormatID formatID,
GLuint stride,
size_t offset);
IndexConversionBufferMtl *getIndexConversionBuffer(const gl::Context *context,
gl::DrawElementsType type,
IndexConversionBufferMtl *getIndexConversionBuffer(ContextMtl *context,
gl::DrawElementsType elemType,
size_t offset);
size_t size() const { return static_cast<size_t>(mState.getSize()); }
private:
angle::Result setDataImpl(const gl::Context *context,
const void *data,
size_t size,
gl::BufferUsage usage);
angle::Result setSubDataImpl(const gl::Context *context,
const void *data,
size_t size,
......@@ -138,6 +156,9 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl
void markConversionBuffersDirty();
void clearConversionBuffers();
bool clientShadowCopyDataNeedSync(ContextMtl *contextMtl);
void ensureShadowCopySyncedFromGPU(ContextMtl *contextMtl);
uint8_t *syncAndObtainShadowCopy(ContextMtl *contextMtl);
// Client side shadow buffer
angle::MemoryBuffer mShadowCopy;
......@@ -145,21 +166,8 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl
// GPU side buffers pool
mtl::BufferPool mBufferPool;
struct VertexConversionBuffer : public ConversionBufferMtl
{
VertexConversionBuffer(const gl::Context *context,
angle::FormatID formatIDIn,
GLuint strideIn,
size_t offsetIn);
// The conversion is identified by the triple of {format, stride, offset}.
angle::FormatID formatID;
GLuint stride;
size_t offset;
};
// A cache of converted vertex data.
std::vector<VertexConversionBuffer> mVertexConversionBuffers;
std::vector<VertexConversionBufferMtl> mVertexConversionBuffers;
std::vector<IndexConversionBufferMtl> mIndexConversionBuffers;
};
......
......@@ -588,7 +588,7 @@ void DisplayMtl::initializeExtensions() const
// Enable this for simple buffer readback testing, but some functionality is missing.
// NOTE(hqle): Support full mapBufferRange extension.
mNativeExtensions.mapBufferOES = true;
mNativeExtensions.mapBufferRange = false;
mNativeExtensions.mapBufferRange = true;
mNativeExtensions.textureStorage = true;
mNativeExtensions.drawBuffers = true;
mNativeExtensions.fragDepth = true;
......@@ -646,6 +646,9 @@ void DisplayMtl::initializeExtensions() const
// GL_APPLE_clip_distance
mNativeExtensions.clipDistanceAPPLE = true;
// GL_NV_pixel_buffer_object
mNativeExtensions.pixelBufferObjectNV = true;
}
void DisplayMtl::initializeTextureCaps() const
......
......@@ -172,6 +172,11 @@ class FramebufferMtl : public FramebufferImpl
const gl::FramebufferAttachment *attachment,
RenderTargetMtl **cachedRenderTarget);
angle::Result readPixelsToPBO(const gl::Context *context,
const gl::Rectangle &area,
const PackPixelsParams &packPixelsParams,
RenderTargetMtl *renderTarget);
// NOTE: we cannot use RenderTargetCache here because it doesn't support separate
// depth & stencil attachments as of now. Separate depth & stencil could be useful to
// save spaces on iOS devices. See doc/PackedDepthStencilSupport.md.
......
......@@ -1068,16 +1068,16 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
uint8_t *pixels)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
if (packPixelsParams.packBuffer)
{
// NOTE(hqle): PBO is not supported atm
ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION);
}
if (!renderTarget)
{
return angle::Result::Continue;
}
if (packPixelsParams.packBuffer)
{
return readPixelsToPBO(context, area, packPixelsParams, renderTarget);
}
mtl::TextureRef texture;
if (mBackbuffer)
{
......@@ -1127,4 +1127,114 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
return angle::Result::Continue;
}
angle::Result FramebufferMtl::readPixelsToPBO(const gl::Context *context,
const gl::Rectangle &area,
const PackPixelsParams &packPixelsParams,
RenderTargetMtl *renderTarget)
{
ASSERT(packPixelsParams.packBuffer);
ASSERT(renderTarget);
ContextMtl *contextMtl = mtl::GetImpl(context);
ANGLE_MTL_CHECK(contextMtl, packPixelsParams.offset <= std::numeric_limits<uint32_t>::max(),
GL_INVALID_OPERATION);
const uint32_t dstBufferOffset = static_cast<uint32_t>(packPixelsParams.offset);
const uint32_t dstBufferRowPitch = packPixelsParams.outputPitch;
const angle::Format &dstAngleFormat = *packPixelsParams.destFormat;
const bool reverseRowOrder = packPixelsParams.reverseRowOrder;
BufferMtl *packBufferMtl = mtl::GetImpl(packPixelsParams.packBuffer);
mtl::BufferRef dstBuffer = packBufferMtl->getCurrentBuffer();
const mtl::Format &readFormat = *renderTarget->getFormat();
const angle::Format &readAngleFormat = readFormat.actualAngleFormat();
mtl::TextureRef texture = renderTarget->getTexture();
if (dstAngleFormat.id != readAngleFormat.id || texture->samples() > 1 ||
(dstBufferOffset % dstAngleFormat.pixelBytes) ||
(dstBufferOffset % mtl::kTextureToBufferBlittingAlignment))
{
const angle::Format *actualDstAngleFormat;
// SRGB is special case: We need to write sRGB values to buffer, not linear values.
switch (readAngleFormat.id)
{
case angle::FormatID::B8G8R8A8_UNORM_SRGB:
case angle::FormatID::R8G8B8_UNORM_SRGB:
case angle::FormatID::R8G8B8A8_UNORM_SRGB:
if (dstAngleFormat.id != readAngleFormat.id)
{
switch (dstAngleFormat.id)
{
case angle::FormatID::B8G8R8A8_UNORM:
actualDstAngleFormat =
&angle::Format::Get(angle::FormatID::B8G8R8A8_UNORM_SRGB);
break;
case angle::FormatID::R8G8B8A8_UNORM:
actualDstAngleFormat =
&angle::Format::Get(angle::FormatID::R8G8B8A8_UNORM_SRGB);
break;
default:
// Unsupported format.
ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_ENUM);
}
break;
}
OS_FALLTHROUGH;
default:
actualDstAngleFormat = &dstAngleFormat;
}
// Use compute shader
mtl::CopyPixelsToBufferParams params;
params.buffer = dstBuffer;
params.bufferStartOffset = dstBufferOffset;
params.bufferRowPitch = dstBufferRowPitch;
params.texture = texture;
params.textureArea = area;
params.textureLevel = renderTarget->getLevelIndex();
params.textureSliceOrDeph = renderTarget->getLayerIndex();
params.reverseTextureRowOrder = reverseRowOrder;
ANGLE_TRY(contextMtl->getDisplay()->getUtils().packPixelsFromTextureToBuffer(
contextMtl, *actualDstAngleFormat, params));
}
else
{
// Use blit command encoder
if (!reverseRowOrder)
{
ANGLE_TRY(mtl::ReadTexturePerSliceBytesToBuffer(
context, texture, dstBufferRowPitch, area, renderTarget->getLevelIndex(),
renderTarget->getLayerIndex(), dstBufferOffset, dstBuffer));
}
else
{
gl::Rectangle srcRowRegion(area.x, area.y, area.width, 1);
int startRow = area.y1() - 1;
uint32_t bufferRowOffset = dstBufferOffset;
// Copy pixels row by row
for (int r = startRow, copiedRows = 0; copiedRows < area.height;
++copiedRows, --r, bufferRowOffset += dstBufferRowPitch)
{
srcRowRegion.y = r;
// Read the pixels data to the buffer's row
ANGLE_TRY(mtl::ReadTexturePerSliceBytesToBuffer(
context, texture, dstBufferRowPitch, srcRowRegion,
renderTarget->getLevelIndex(), renderTarget->getLayerIndex(), bufferRowOffset,
dstBuffer));
}
}
}
return angle::Result::Continue;
}
}
......@@ -513,10 +513,11 @@ angle::Result VertexArrayMtl::convertIndexBuffer(const gl::Context *glContext,
ASSERT((offset % mtl::kIndexBufferOffsetAlignment) != 0 ||
indexType == gl::DrawElementsType::UnsignedByte);
BufferMtl *idxBuffer = mtl::GetImpl(getState().getElementArrayBuffer());
ContextMtl *contextMtl = mtl::GetImpl(glContext);
BufferMtl *idxBuffer = mtl::GetImpl(getState().getElementArrayBuffer());
IndexConversionBufferMtl *conversion =
idxBuffer->getIndexConversionBuffer(glContext, indexType, offset);
idxBuffer->getIndexConversionBuffer(contextMtl, indexType, offset);
// Has the content of the buffer has changed since last conversion?
if (!conversion->dirty)
......@@ -594,16 +595,16 @@ angle::Result VertexArrayMtl::convertVertexBuffer(const gl::Context *glContext,
size_t attribIndex,
const mtl::VertexFormat &srcVertexFormat)
{
ContextMtl *contextMtl = mtl::GetImpl(glContext);
const angle::Format &intendedAngleFormat = srcVertexFormat.intendedAngleFormat();
ConversionBufferMtl *conversion = srcBuffer->getVertexConversionBuffer(
glContext, intendedAngleFormat.id, binding.getStride(), binding.getOffset());
contextMtl, intendedAngleFormat.id, binding.getStride(), binding.getOffset());
// Has the content of the buffer has changed since last conversion?
if (!conversion->dirty)
{
ContextMtl *contextMtl = mtl::GetImpl(glContext);
// Buffer's data hasn't been changed. Re-use last converted results
GLuint stride;
const mtl::VertexFormat &vertexFormat =
......@@ -646,7 +647,7 @@ angle::Result VertexArrayMtl::convertVertexBufferCPU(const gl::Context *glContex
return angle::Result::Continue;
}
const uint8_t *srcBytes = srcBuffer->getClientShadowCopyData(glContext);
const uint8_t *srcBytes = srcBuffer->getClientShadowCopyData(contextMtl);
ANGLE_CHECK_GL_ALLOC(contextMtl, srcBytes);
srcBytes += binding.getOffset();
......
......@@ -505,6 +505,17 @@ class BlitCommandEncoder final : public CommandEncoder
MTLOrigin dstOrigin,
MTLBlitOption blitOption);
BlitCommandEncoder &copyTextureToBuffer(const TextureRef &src,
uint32_t srcSlice,
uint32_t srcLevel,
MTLOrigin srcOrigin,
MTLSize srcSize,
const BufferRef &dst,
size_t dstOffset,
size_t dstBytesPerRow,
size_t dstBytesPerImage,
MTLBlitOption blitOption);
BlitCommandEncoder &copyTexture(const TextureRef &src,
uint32_t srcSlice,
uint32_t srcLevel,
......
......@@ -1605,6 +1605,40 @@ BlitCommandEncoder &BlitCommandEncoder::copyBufferToTexture(const BufferRef &src
return *this;
}
BlitCommandEncoder &BlitCommandEncoder::copyTextureToBuffer(const TextureRef &src,
uint32_t srcSlice,
uint32_t srcLevel,
MTLOrigin srcOrigin,
MTLSize srcSize,
const BufferRef &dst,
size_t dstOffset,
size_t dstBytesPerRow,
size_t dstBytesPerImage,
MTLBlitOption blitOption)
{
if (!src || !dst)
{
return *this;
}
cmdBuffer().setReadDependency(src);
cmdBuffer().setWriteDependency(dst);
[get() copyFromTexture:src->get()
sourceSlice:srcSlice
sourceLevel:srcLevel
sourceOrigin:srcOrigin
sourceSize:srcSize
toBuffer:dst->get()
destinationOffset:dstOffset
destinationBytesPerRow:dstBytesPerRow
destinationBytesPerImage:dstBytesPerImage
options:blitOption];
return *this;
}
BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &src,
uint32_t srcStartSlice,
uint32_t srcStartLevel,
......
......@@ -114,7 +114,8 @@ constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 256;
#else
constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 4;
#endif
constexpr uint32_t kIndexBufferOffsetAlignment = 4;
constexpr uint32_t kIndexBufferOffsetAlignment = 4;
constexpr uint32_t kTextureToBufferBlittingAlignment = 256;
// Front end binding limits
constexpr uint32_t kMaxGLSamplerBindings = 2 * kMaxShaderSamplers;
......@@ -138,6 +139,21 @@ constexpr size_t kOcclusionQueryResultSize = sizeof(uint64_t);
// NOTE(hqle): Support ES 3.0.
constexpr gl::Version kMaxSupportedGLVersion = gl::Version(2, 0);
// Work-around the enum is not available on macOS
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
constexpr MTLBlitOption kBlitOptionRowLinearPVRTC = MTLBlitOptionNone;
#else
constexpr MTLBlitOption kBlitOptionRowLinearPVRTC = MTLBlitOptionRowLinearPVRTC;
#endif
enum class PixelType
{
Int,
UInt,
Float,
EnumCount,
};
template <typename T>
struct ImplTypeHelper;
......
......@@ -128,6 +128,33 @@ struct IndexGenerationParams
uint32_t dstOffset;
};
struct CopyPixelsCommonParams
{
BufferRef buffer;
uint32_t bufferStartOffset = 0;
uint32_t bufferRowPitch = 0;
TextureRef texture;
};
struct CopyPixelsFromBufferParams : CopyPixelsCommonParams
{
uint32_t bufferDepthPitch = 0;
// z offset is:
// - slice index if texture is array.
// - depth if texture is 3d.
gl::Box textureArea;
};
struct CopyPixelsToBufferParams : CopyPixelsCommonParams
{
gl::Rectangle textureArea;
uint32_t textureLevel = 0;
uint32_t textureSliceOrDeph = 0;
bool reverseTextureRowOrder;
};
// Utils class for clear & blitting
class ClearUtils final : angle::NonCopyable
{
......@@ -353,6 +380,40 @@ class MipmapUtils final : angle::NonCopyable
AutoObjCPtr<id<MTLComputePipelineState>> m3DMipGeneratorPipeline;
};
// Util class for handling pixels copy between buffers and textures
class CopyPixelsUtils
{
public:
CopyPixelsUtils() = default;
CopyPixelsUtils(const std::string &readShaderName, const std::string &writeShaderName);
CopyPixelsUtils(const CopyPixelsUtils &src);
void onDestroy();
angle::Result unpackPixelsFromBufferToTexture(ContextMtl *contextMtl,
const angle::Format &srcAngleFormat,
const CopyPixelsFromBufferParams &params);
angle::Result packPixelsFromTextureToBuffer(ContextMtl *contextMtl,
const angle::Format &dstAngleFormat,
const CopyPixelsToBufferParams &params);
private:
AutoObjCPtr<id<MTLComputePipelineState>> getPixelsCopyPipeline(ContextMtl *contextMtl,
const angle::Format &angleFormat,
const TextureRef &texture,
bool bufferWrite);
// Copy pixels between buffer and texture compute pipelines:
// - First dimension: pixel format.
// - Second dimension: texture type * (buffer read/write flag)
using PixelsCopyPipelineArray = std::array<
std::array<AutoObjCPtr<id<MTLComputePipelineState>>, mtl_shader::kTextureTypeCount * 2>,
angle::kNumANGLEFormats>;
PixelsCopyPipelineArray mPixelsCopyPipelineCaches;
const std::string mReadShaderName;
const std::string mWriteShaderName;
};
// RenderUtils: container class of various util classes above
class RenderUtils : public Context, angle::NonCopyable
{
......@@ -411,6 +472,13 @@ class RenderUtils : public Context, angle::NonCopyable
bool sRGBMipmap,
gl::TexLevelArray<mtl::TextureRef> *mipmapOutputViews);
angle::Result unpackPixelsFromBufferToTexture(ContextMtl *contextMtl,
const angle::Format &srcAngleFormat,
const CopyPixelsFromBufferParams &params);
angle::Result packPixelsFromTextureToBuffer(ContextMtl *contextMtl,
const angle::Format &dstAngleFormat,
const CopyPixelsToBufferParams &params);
private:
// override ErrorHandler
void handleError(GLenum error,
......@@ -428,6 +496,7 @@ class RenderUtils : public Context, angle::NonCopyable
IndexGeneratorUtils mIndexUtils;
VisibilityResultUtils mVisibilityResultUtils;
MipmapUtils mMipmapUtils;
std::array<CopyPixelsUtils, angle::EnumSize<PixelType>()> mCopyPixelsUtils;
};
} // namespace mtl
......
......@@ -56,6 +56,15 @@ angle::Result ReadTexturePerSliceBytes(const gl::Context *context,
uint32_t sliceOrDepth,
uint8_t *dataOut);
angle::Result ReadTexturePerSliceBytesToBuffer(const gl::Context *context,
const TextureRef &texture,
size_t bytesPerRow,
const gl::Rectangle &fromRegion,
uint32_t mipLevel,
uint32_t sliceOrDepth,
uint32_t dstOffset,
const BufferRef &dstBuffer);
MTLViewport GetViewport(const gl::Rectangle &rect, double znear = 0, double zfar = 1);
MTLViewport GetViewportFlipY(const gl::Rectangle &rect,
NSUInteger screenHeight,
......@@ -132,6 +141,8 @@ bool IsFormatEmulated(const mtl::Format &mtlFormat);
// has alpha channel.
MTLClearColor EmulatedAlphaClearColor(MTLClearColor color, MTLColorWriteMask colorMask);
gl::Box MTLRegionToGLBox(const MTLRegion &mtlRegion);
NS_ASSUME_NONNULL_END
} // namespace mtl
} // namespace rx
......
......@@ -321,6 +321,42 @@ angle::Result ReadTexturePerSliceBytes(const gl::Context *context,
return angle::Result::Continue;
}
angle::Result ReadTexturePerSliceBytesToBuffer(const gl::Context *context,
const TextureRef &texture,
size_t bytesPerRow,
const gl::Rectangle &fromRegion,
uint32_t mipLevel,
uint32_t sliceOrDepth,
uint32_t dstOffset,
const BufferRef &dstBuffer)
{
ASSERT(texture && texture->valid());
ContextMtl *contextMtl = mtl::GetImpl(context);
GLint layer = 0;
GLint startDepth = 0;
switch (texture->textureType())
{
case MTLTextureTypeCube:
case MTLTextureType2DArray:
layer = sliceOrDepth;
break;
case MTLTextureType3D:
startDepth = sliceOrDepth;
break;
default:
break;
}
MTLRegion mtlRegion = MTLRegionMake3D(fromRegion.x, fromRegion.y, startDepth, fromRegion.width,
fromRegion.height, 1);
BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
blitEncoder->copyTextureToBuffer(texture, layer, mipLevel, mtlRegion.origin, mtlRegion.size,
dstBuffer, dstOffset, bytesPerRow, 0, MTLBlitOptionNone);
return angle::Result::Continue;
}
MTLViewport GetViewport(const gl::Rectangle &rect, double znear, double zfar)
{
MTLViewport re;
......@@ -783,5 +819,12 @@ MTLClearColor EmulatedAlphaClearColor(MTLClearColor color, MTLColorWriteMask col
return re;
}
gl::Box MTLRegionToGLBox(const MTLRegion &mtlRegion)
{
return gl::Box(static_cast<int>(mtlRegion.origin.x), static_cast<int>(mtlRegion.origin.y),
static_cast<int>(mtlRegion.origin.z), static_cast<int>(mtlRegion.size.width),
static_cast<int>(mtlRegion.size.height), static_cast<int>(mtlRegion.size.depth));
}
} // namespace mtl
} // namespace rx
......@@ -11,6 +11,7 @@
#include <array>
#include "test_utils/gl_raii.h"
#include "util/random_utils.h"
using namespace angle;
......@@ -61,21 +62,23 @@ TEST_P(ReadPixelsTest, OutOfBounds)
}
}
class ReadPixelsPBOTest : public ReadPixelsTest
class ReadPixelsPBONVTest : public ReadPixelsTest
{
protected:
ReadPixelsPBOTest() : mPBO(0), mTexture(0), mFBO(0) {}
ReadPixelsPBONVTest() : mPBO(0), mTexture(0), mFBO(0) {}
void testSetUp() override
{
glGenBuffers(1, &mPBO);
glGenFramebuffers(1, &mFBO);
Reset(4 * getWindowWidth() * getWindowHeight(), 4, 1);
Reset(4 * getWindowWidth() * getWindowHeight(), 4, 4);
}
void Reset(GLuint bufferSize, GLuint fboWidth, GLuint fboHeight)
virtual void Reset(GLuint bufferSize, GLuint fboWidth, GLuint fboHeight)
{
ANGLE_SKIP_TEST_IF(!hasPBOExts());
glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
glBufferData(GL_PIXEL_PACK_BUFFER, bufferSize, nullptr, GL_STATIC_DRAW);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
......@@ -83,7 +86,9 @@ class ReadPixelsPBOTest : public ReadPixelsTest
glDeleteTextures(1, &mTexture);
glGenTextures(1, &mTexture);
glBindTexture(GL_TEXTURE_2D, mTexture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, fboWidth, fboHeight);
glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, fboWidth, fboHeight);
mFBOWidth = fboWidth;
mFBOHeight = fboHeight;
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
......@@ -99,9 +104,183 @@ class ReadPixelsPBOTest : public ReadPixelsTest
glDeleteFramebuffers(1, &mFBO);
}
GLuint mPBO = 0;
GLuint mTexture = 0;
GLuint mFBO = 0;
bool hasPBOExts() const
{
return IsGLExtensionEnabled("GL_NV_pixel_buffer_object") &&
IsGLExtensionEnabled("GL_EXT_texture_storage");
}
GLuint mPBO = 0;
GLuint mTexture = 0;
GLuint mFBO = 0;
GLuint mFBOWidth = 0;
GLuint mFBOHeight = 0;
};
// Test basic usage of PBOs.
TEST_P(ReadPixelsPBONVTest, Basic)
{
ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") ||
!IsGLExtensionEnabled("GL_OES_mapbuffer"));
// http://anglebug.com/5022
ANGLE_SKIP_TEST_IF(IsWindows() && IsDesktopOpenGL());
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Clear last pixel to green
glScissor(15, 15, 1, 1);
glEnable(GL_SCISSOR_TEST);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);
void *mappedPtr = glMapBufferRangeEXT(GL_PIXEL_PACK_BUFFER, 0, 32, GL_MAP_READ_BIT);
GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(GLColor::red, dataColor[0]);
EXPECT_EQ(GLColor::red, dataColor[16 * 16 - 2]);
EXPECT_EQ(GLColor::green, dataColor[16 * 16 - 1]);
glUnmapBufferOES(GL_PIXEL_PACK_BUFFER);
EXPECT_GL_NO_ERROR();
}
// Test that calling SubData preserves PBO data.
TEST_P(ReadPixelsPBONVTest, SubDataPreservesContents)
{
ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") ||
!IsGLExtensionEnabled("GL_OES_mapbuffer"));
// anglebug.com/2185
ANGLE_SKIP_TEST_IF(IsOSX() && IsNVIDIA() && IsDesktopOpenGL());
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);
unsigned char data[4] = {1, 2, 3, 4};
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, mPBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, 4, data);
void *mappedPtr = glMapBufferRangeEXT(GL_ARRAY_BUFFER, 0, 32, GL_MAP_READ_BIT);
GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(GLColor(1, 2, 3, 4), dataColor[0]);
EXPECT_EQ(GLColor::red, dataColor[1]);
glUnmapBufferOES(GL_ARRAY_BUFFER);
EXPECT_GL_NO_ERROR();
}
// Test that calling ReadPixels with GL_DYNAMIC_DRAW buffer works
TEST_P(ReadPixelsPBONVTest, DynamicPBO)
{
ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") ||
!IsGLExtensionEnabled("GL_OES_mapbuffer"));
// anglebug.com/2185
ANGLE_SKIP_TEST_IF(IsOSX() && IsNVIDIA() && IsDesktopOpenGL());
glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
glBufferData(GL_PIXEL_PACK_BUFFER, 4 * getWindowWidth() * getWindowHeight(), nullptr,
GL_DYNAMIC_DRAW);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);
unsigned char data[4] = {1, 2, 3, 4};
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, mPBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, 4, data);
void *mappedPtr = glMapBufferRangeEXT(GL_ARRAY_BUFFER, 0, 32, GL_MAP_READ_BIT);
GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(GLColor(1, 2, 3, 4), dataColor[0]);
EXPECT_EQ(GLColor::red, dataColor[1]);
glUnmapBufferOES(GL_ARRAY_BUFFER);
EXPECT_GL_NO_ERROR();
}
TEST_P(ReadPixelsPBONVTest, ReadFromFBO)
{
ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") ||
!IsGLExtensionEnabled("GL_OES_mapbuffer"));
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
glViewport(0, 0, mFBOWidth, mFBOHeight);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Clear last pixel to green
glScissor(mFBOWidth - 1, mFBOHeight - 1, 1, 1);
glEnable(GL_SCISSOR_TEST);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
glReadPixels(0, 0, mFBOWidth, mFBOHeight, GL_RGBA, GL_UNSIGNED_BYTE, 0);
void *mappedPtr =
glMapBufferRangeEXT(GL_PIXEL_PACK_BUFFER, 0, 4 * mFBOWidth * mFBOHeight, GL_MAP_READ_BIT);
GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(GLColor::red, dataColor[0]);
EXPECT_EQ(GLColor::red, dataColor[mFBOWidth * mFBOHeight - 2]);
EXPECT_EQ(GLColor::green, dataColor[mFBOWidth * mFBOHeight - 1]);
glUnmapBufferOES(GL_PIXEL_PACK_BUFFER);
EXPECT_GL_NO_ERROR();
}
class ReadPixelsPBOTest : public ReadPixelsPBONVTest
{
protected:
ReadPixelsPBOTest() : ReadPixelsPBONVTest() {}
void testSetUp() override
{
glGenBuffers(1, &mPBO);
glGenFramebuffers(1, &mFBO);
Reset(4 * getWindowWidth() * getWindowHeight(), 4, 1);
}
void Reset(GLuint bufferSize, GLuint fboWidth, GLuint fboHeight) override
{
glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
glBufferData(GL_PIXEL_PACK_BUFFER, bufferSize, nullptr, GL_STATIC_DRAW);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glDeleteTextures(1, &mTexture);
glGenTextures(1, &mTexture);
glBindTexture(GL_TEXTURE_2D, mTexture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, fboWidth, fboHeight);
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
ASSERT_GL_NO_ERROR();
}
};
// Test basic usage of PBOs.
......@@ -696,6 +875,7 @@ TEST_P(ReadPixelsErrorTest, ReadBufferIsNone)
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST_ES2(ReadPixelsTest);
ANGLE_INSTANTIATE_TEST_ES2(ReadPixelsPBONVTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsPBOTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsPBODrawTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsMultisampleTest);
......
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