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 ...@@ -28,7 +28,7 @@ namespace rx
// Conversion buffers hold translated index and vertex data. // Conversion buffers hold translated index and vertex data.
struct ConversionBufferMtl struct ConversionBufferMtl
{ {
ConversionBufferMtl(const gl::Context *context, size_t initialSize, size_t alignment); ConversionBufferMtl(ContextMtl *context, size_t initialSize, size_t alignment);
~ConversionBufferMtl(); ~ConversionBufferMtl();
// One state value determines if we need to re-stream vertex data. // One state value determines if we need to re-stream vertex data.
...@@ -42,13 +42,24 @@ struct ConversionBufferMtl ...@@ -42,13 +42,24 @@ struct ConversionBufferMtl
size_t convertedOffset; size_t convertedOffset;
}; };
struct IndexConversionBufferMtl : public ConversionBufferMtl struct VertexConversionBufferMtl : public ConversionBufferMtl
{ {
IndexConversionBufferMtl(const gl::Context *context, VertexConversionBufferMtl(ContextMtl *context,
gl::DrawElementsType type, angle::FormatID formatIDIn,
GLuint strideIn,
size_t offsetIn); size_t offsetIn);
const gl::DrawElementsType type; // The conversion is identified by the triple of {format, stride, offset}.
angle::FormatID formatID;
GLuint stride;
size_t offset;
};
struct IndexConversionBufferMtl : public ConversionBufferMtl
{
IndexConversionBufferMtl(ContextMtl *context, gl::DrawElementsType elemType, size_t offsetIn);
const gl::DrawElementsType elemType;
const size_t offset; const size_t offset;
}; };
...@@ -108,25 +119,32 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl ...@@ -108,25 +119,32 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl
bool primitiveRestartEnabled, bool primitiveRestartEnabled,
gl::IndexRange *outRange) override; 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 offset,
size_t count, 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, angle::FormatID formatID,
GLuint stride, GLuint stride,
size_t offset); size_t offset);
IndexConversionBufferMtl *getIndexConversionBuffer(const gl::Context *context, IndexConversionBufferMtl *getIndexConversionBuffer(ContextMtl *context,
gl::DrawElementsType type, gl::DrawElementsType elemType,
size_t offset); size_t offset);
size_t size() const { return static_cast<size_t>(mState.getSize()); } size_t size() const { return static_cast<size_t>(mState.getSize()); }
private: private:
angle::Result setDataImpl(const gl::Context *context,
const void *data,
size_t size,
gl::BufferUsage usage);
angle::Result setSubDataImpl(const gl::Context *context, angle::Result setSubDataImpl(const gl::Context *context,
const void *data, const void *data,
size_t size, size_t size,
...@@ -138,6 +156,9 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl ...@@ -138,6 +156,9 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl
void markConversionBuffersDirty(); void markConversionBuffersDirty();
void clearConversionBuffers(); void clearConversionBuffers();
bool clientShadowCopyDataNeedSync(ContextMtl *contextMtl);
void ensureShadowCopySyncedFromGPU(ContextMtl *contextMtl);
uint8_t *syncAndObtainShadowCopy(ContextMtl *contextMtl);
// Client side shadow buffer // Client side shadow buffer
angle::MemoryBuffer mShadowCopy; angle::MemoryBuffer mShadowCopy;
...@@ -145,21 +166,8 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl ...@@ -145,21 +166,8 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl
// GPU side buffers pool // GPU side buffers pool
mtl::BufferPool mBufferPool; 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. // A cache of converted vertex data.
std::vector<VertexConversionBuffer> mVertexConversionBuffers; std::vector<VertexConversionBufferMtl> mVertexConversionBuffers;
std::vector<IndexConversionBufferMtl> mIndexConversionBuffers; std::vector<IndexConversionBufferMtl> mIndexConversionBuffers;
}; };
......
...@@ -588,7 +588,7 @@ void DisplayMtl::initializeExtensions() const ...@@ -588,7 +588,7 @@ void DisplayMtl::initializeExtensions() const
// Enable this for simple buffer readback testing, but some functionality is missing. // Enable this for simple buffer readback testing, but some functionality is missing.
// NOTE(hqle): Support full mapBufferRange extension. // NOTE(hqle): Support full mapBufferRange extension.
mNativeExtensions.mapBufferOES = true; mNativeExtensions.mapBufferOES = true;
mNativeExtensions.mapBufferRange = false; mNativeExtensions.mapBufferRange = true;
mNativeExtensions.textureStorage = true; mNativeExtensions.textureStorage = true;
mNativeExtensions.drawBuffers = true; mNativeExtensions.drawBuffers = true;
mNativeExtensions.fragDepth = true; mNativeExtensions.fragDepth = true;
...@@ -646,6 +646,9 @@ void DisplayMtl::initializeExtensions() const ...@@ -646,6 +646,9 @@ void DisplayMtl::initializeExtensions() const
// GL_APPLE_clip_distance // GL_APPLE_clip_distance
mNativeExtensions.clipDistanceAPPLE = true; mNativeExtensions.clipDistanceAPPLE = true;
// GL_NV_pixel_buffer_object
mNativeExtensions.pixelBufferObjectNV = true;
} }
void DisplayMtl::initializeTextureCaps() const void DisplayMtl::initializeTextureCaps() const
......
...@@ -172,6 +172,11 @@ class FramebufferMtl : public FramebufferImpl ...@@ -172,6 +172,11 @@ class FramebufferMtl : public FramebufferImpl
const gl::FramebufferAttachment *attachment, const gl::FramebufferAttachment *attachment,
RenderTargetMtl **cachedRenderTarget); 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 // 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 // depth & stencil attachments as of now. Separate depth & stencil could be useful to
// save spaces on iOS devices. See doc/PackedDepthStencilSupport.md. // save spaces on iOS devices. See doc/PackedDepthStencilSupport.md.
......
...@@ -1068,16 +1068,16 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context, ...@@ -1068,16 +1068,16 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
uint8_t *pixels) uint8_t *pixels)
{ {
ContextMtl *contextMtl = mtl::GetImpl(context); 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) if (!renderTarget)
{ {
return angle::Result::Continue; return angle::Result::Continue;
} }
if (packPixelsParams.packBuffer)
{
return readPixelsToPBO(context, area, packPixelsParams, renderTarget);
}
mtl::TextureRef texture; mtl::TextureRef texture;
if (mBackbuffer) if (mBackbuffer)
{ {
...@@ -1127,4 +1127,114 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context, ...@@ -1127,4 +1127,114 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
return angle::Result::Continue; 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;
}
} }
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "common/mathutil.h" #include "common/mathutil.h"
#include "image_util/imageformats.h" #include "image_util/imageformats.h"
#include "libANGLE/Surface.h" #include "libANGLE/Surface.h"
#include "libANGLE/renderer/metal/BufferMtl.h"
#include "libANGLE/renderer/metal/ContextMtl.h" #include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/DisplayMtl.h" #include "libANGLE/renderer/metal/DisplayMtl.h"
#include "libANGLE/renderer/metal/FrameBufferMtl.h" #include "libANGLE/renderer/metal/FrameBufferMtl.h"
...@@ -1283,10 +1284,7 @@ angle::Result TextureMtl::setSubImageImpl(const gl::Context *context, ...@@ -1283,10 +1284,7 @@ angle::Result TextureMtl::setSubImageImpl(const gl::Context *context,
gl::Buffer *unpackBuffer, gl::Buffer *unpackBuffer,
const uint8_t *oriPixels) const uint8_t *oriPixels)
{ {
// NOTE(hqle): Support PBO if (!oriPixels && !unpackBuffer)
ASSERT(!unpackBuffer);
if (!oriPixels)
{ {
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -1354,13 +1352,47 @@ angle::Result TextureMtl::setPerSliceSubImage(const gl::Context *context, ...@@ -1354,13 +1352,47 @@ angle::Result TextureMtl::setPerSliceSubImage(const gl::Context *context,
unpackBuffer, pixels, image); unpackBuffer, pixels, image);
} }
// NOTE(hqle): Support PBO // No conversion needed.
ASSERT(!unpackBuffer); ContextMtl *contextMtl = mtl::GetImpl(context);
// Upload texture data directly if (unpackBuffer)
ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea, 0, slice, pixels, {
pixelsRowPitch, pixelsDepthPitch, image)); uintptr_t offset = reinterpret_cast<uintptr_t>(pixels);
if (offset % mFormat.actualAngleFormat().pixelBytes)
{
// offset is not divisible by pixelByte, use convertAndSetPerSliceSubImage() function.
return convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type,
pixelsAngleFormat, pixelsRowPitch,
pixelsDepthPitch, unpackBuffer, pixels, image);
}
BufferMtl *unpackBufferMtl = mtl::GetImpl(unpackBuffer);
if (mFormat.hasDepthAndStencilBits())
{
// NOTE(hqle): packed depth & stencil texture cannot copy from buffer directly, needs
// to split its depth & stencil data and copy separately.
const uint8_t *clientData = unpackBufferMtl->getClientShadowCopyData(contextMtl);
clientData += offset;
ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea, 0, slice,
clientData, pixelsRowPitch, pixelsDepthPitch, image));
}
else
{
// Use blit encoder to copy
mtl::BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
blitEncoder->copyBufferToTexture(
unpackBufferMtl->getCurrentBuffer(), offset, pixelsRowPitch, pixelsDepthPitch,
mtlArea.size, image, slice, 0, mtlArea.origin,
mFormat.isPVRTC() ? mtl::kBlitOptionRowLinearPVRTC : MTLBlitOptionNone);
}
}
else
{
// Upload texture data directly
ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea, 0, slice,
pixels, pixelsRowPitch, pixelsDepthPitch, image));
}
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -1380,11 +1412,55 @@ angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *conte ...@@ -1380,11 +1412,55 @@ angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *conte
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
// NOTE(hqle): Support PBO if (unpackBuffer)
ASSERT(!unpackBuffer); {
ANGLE_MTL_CHECK(contextMtl,
reinterpret_cast<uintptr_t>(pixels) <= std::numeric_limits<uint32_t>::max(),
GL_INVALID_OPERATION);
uint32_t offset = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pixels));
LoadImageFunctionInfo loadFunctionInfo = BufferMtl *unpackBufferMtl = mtl::GetImpl(unpackBuffer);
mFormat.textureLoadFunctions ? mFormat.textureLoadFunctions(type) : LoadImageFunctionInfo(); if (!mFormat.getCaps().writable || mFormat.hasDepthOrStencilBits() ||
mFormat.intendedAngleFormat().isBlock)
{
// Unsupported format, use CPU path.
const uint8_t *clientData = unpackBufferMtl->getClientShadowCopyData(contextMtl);
clientData += offset;
ANGLE_TRY(convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type,
pixelsAngleFormat, pixelsRowPitch,
pixelsDepthPitch, nullptr, clientData, image));
}
else
{
// Use compute shader
mtl::CopyPixelsFromBufferParams params;
params.buffer = unpackBufferMtl->getCurrentBuffer();
params.bufferStartOffset = offset;
params.bufferRowPitch = static_cast<uint32_t>(pixelsRowPitch);
params.bufferDepthPitch = static_cast<uint32_t>(pixelsDepthPitch);
params.texture = image;
params.textureArea = mtl::MTLRegionToGLBox(mtlArea);
// If texture is not array, slice must be zero, if texture is array, mtlArea.origin.z
// must be zero.
// This is because this function uses Metal convention: where slice is only used for
// array textures, and z layer of mtlArea.origin is only used for 3D textures.
ASSERT(slice == 0 || params.textureArea.z == 0);
// For mtl::RenderUtils we convert to OpenGL convention: z layer is used as either array
// texture's slice or 3D texture's layer index.
params.textureArea.z += slice;
ANGLE_TRY(contextMtl->getDisplay()->getUtils().unpackPixelsFromBufferToTexture(
contextMtl, pixelsAngleFormat, params));
}
} // if (unpackBuffer)
else
{
LoadImageFunctionInfo loadFunctionInfo = mFormat.textureLoadFunctions
? mFormat.textureLoadFunctions(type)
: LoadImageFunctionInfo();
const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId); const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId);
const size_t dstRowPitch = dstFormat.pixelBytes * mtlArea.size.width; const size_t dstRowPitch = dstFormat.pixelBytes * mtlArea.size.width;
...@@ -1396,16 +1472,18 @@ angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *conte ...@@ -1396,16 +1472,18 @@ angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *conte
// Need to create a buffer to hold entire decompressed image. // Need to create a buffer to hold entire decompressed image.
const size_t dstDepthPitch = dstRowPitch * mtlArea.size.height; const size_t dstDepthPitch = dstRowPitch * mtlArea.size.height;
angle::MemoryBuffer decompressBuf; angle::MemoryBuffer decompressBuf;
ANGLE_CHECK_GL_ALLOC(contextMtl, decompressBuf.resize(dstDepthPitch * mtlArea.size.depth)); ANGLE_CHECK_GL_ALLOC(contextMtl,
decompressBuf.resize(dstDepthPitch * mtlArea.size.depth));
// Decompress // Decompress
loadFunctionInfo.loadFunction(mtlArea.size.width, mtlArea.size.height, mtlArea.size.depth, loadFunctionInfo.loadFunction(
pixels, pixelsRowPitch, pixelsDepthPitch, mtlArea.size.width, mtlArea.size.height, mtlArea.size.depth, pixels, pixelsRowPitch,
decompressBuf.data(), dstRowPitch, dstDepthPitch); pixelsDepthPitch, decompressBuf.data(), dstRowPitch, dstDepthPitch);
// Upload to texture // Upload to texture
ANGLE_TRY(UploadTextureContents(context, dstFormat, mtlArea, 0, slice, decompressBuf.data(), ANGLE_TRY(UploadTextureContents(context, dstFormat, mtlArea, 0, slice,
dstRowPitch, dstDepthPitch, image)); decompressBuf.data(), dstRowPitch, dstDepthPitch,
image));
} // if (mFormat.intendedAngleFormat().isBlock) } // if (mFormat.intendedAngleFormat().isBlock)
else else
{ {
...@@ -1427,13 +1505,13 @@ angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *conte ...@@ -1427,13 +1505,13 @@ angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *conte
// Convert pixels // Convert pixels
if (loadFunctionInfo.loadFunction) if (loadFunctionInfo.loadFunction)
{ {
loadFunctionInfo.loadFunction(mtlRow.size.width, 1, 1, psrc, pixelsRowPitch, 0, loadFunctionInfo.loadFunction(mtlRow.size.width, 1, 1, psrc, pixelsRowPitch,
conversionRow.data(), dstRowPitch, 0); 0, conversionRow.data(), dstRowPitch, 0);
} }
else if (mFormat.hasDepthOrStencilBits()) else if (mFormat.hasDepthOrStencilBits())
{ {
ConvertDepthStencilData(mtlRow.size, pixelsAngleFormat, pixelsRowPitch, 0, psrc, ConvertDepthStencilData(mtlRow.size, pixelsAngleFormat, pixelsRowPitch, 0,
dstFormat, nullptr, dstRowPitch, 0, psrc, dstFormat, nullptr, dstRowPitch, 0,
conversionRow.data()); conversionRow.data());
} }
else else
...@@ -1452,6 +1530,7 @@ angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *conte ...@@ -1452,6 +1530,7 @@ angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *conte
} }
} }
} // if (mFormat.intendedAngleFormat().isBlock) } // if (mFormat.intendedAngleFormat().isBlock)
} // if (unpackBuffer)
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -513,10 +513,11 @@ angle::Result VertexArrayMtl::convertIndexBuffer(const gl::Context *glContext, ...@@ -513,10 +513,11 @@ angle::Result VertexArrayMtl::convertIndexBuffer(const gl::Context *glContext,
ASSERT((offset % mtl::kIndexBufferOffsetAlignment) != 0 || ASSERT((offset % mtl::kIndexBufferOffsetAlignment) != 0 ||
indexType == gl::DrawElementsType::UnsignedByte); indexType == gl::DrawElementsType::UnsignedByte);
ContextMtl *contextMtl = mtl::GetImpl(glContext);
BufferMtl *idxBuffer = mtl::GetImpl(getState().getElementArrayBuffer()); BufferMtl *idxBuffer = mtl::GetImpl(getState().getElementArrayBuffer());
IndexConversionBufferMtl *conversion = IndexConversionBufferMtl *conversion =
idxBuffer->getIndexConversionBuffer(glContext, indexType, offset); idxBuffer->getIndexConversionBuffer(contextMtl, indexType, offset);
// Has the content of the buffer has changed since last conversion? // Has the content of the buffer has changed since last conversion?
if (!conversion->dirty) if (!conversion->dirty)
...@@ -594,16 +595,16 @@ angle::Result VertexArrayMtl::convertVertexBuffer(const gl::Context *glContext, ...@@ -594,16 +595,16 @@ angle::Result VertexArrayMtl::convertVertexBuffer(const gl::Context *glContext,
size_t attribIndex, size_t attribIndex,
const mtl::VertexFormat &srcVertexFormat) const mtl::VertexFormat &srcVertexFormat)
{ {
ContextMtl *contextMtl = mtl::GetImpl(glContext);
const angle::Format &intendedAngleFormat = srcVertexFormat.intendedAngleFormat(); const angle::Format &intendedAngleFormat = srcVertexFormat.intendedAngleFormat();
ConversionBufferMtl *conversion = srcBuffer->getVertexConversionBuffer( 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? // Has the content of the buffer has changed since last conversion?
if (!conversion->dirty) if (!conversion->dirty)
{ {
ContextMtl *contextMtl = mtl::GetImpl(glContext);
// Buffer's data hasn't been changed. Re-use last converted results // Buffer's data hasn't been changed. Re-use last converted results
GLuint stride; GLuint stride;
const mtl::VertexFormat &vertexFormat = const mtl::VertexFormat &vertexFormat =
...@@ -646,7 +647,7 @@ angle::Result VertexArrayMtl::convertVertexBufferCPU(const gl::Context *glContex ...@@ -646,7 +647,7 @@ angle::Result VertexArrayMtl::convertVertexBufferCPU(const gl::Context *glContex
return angle::Result::Continue; return angle::Result::Continue;
} }
const uint8_t *srcBytes = srcBuffer->getClientShadowCopyData(glContext); const uint8_t *srcBytes = srcBuffer->getClientShadowCopyData(contextMtl);
ANGLE_CHECK_GL_ALLOC(contextMtl, srcBytes); ANGLE_CHECK_GL_ALLOC(contextMtl, srcBytes);
srcBytes += binding.getOffset(); srcBytes += binding.getOffset();
......
...@@ -505,6 +505,17 @@ class BlitCommandEncoder final : public CommandEncoder ...@@ -505,6 +505,17 @@ class BlitCommandEncoder final : public CommandEncoder
MTLOrigin dstOrigin, MTLOrigin dstOrigin,
MTLBlitOption blitOption); 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, BlitCommandEncoder &copyTexture(const TextureRef &src,
uint32_t srcSlice, uint32_t srcSlice,
uint32_t srcLevel, uint32_t srcLevel,
......
...@@ -1605,6 +1605,40 @@ BlitCommandEncoder &BlitCommandEncoder::copyBufferToTexture(const BufferRef &src ...@@ -1605,6 +1605,40 @@ BlitCommandEncoder &BlitCommandEncoder::copyBufferToTexture(const BufferRef &src
return *this; 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, BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &src,
uint32_t srcStartSlice, uint32_t srcStartSlice,
uint32_t srcStartLevel, uint32_t srcStartLevel,
......
...@@ -115,6 +115,7 @@ constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 256; ...@@ -115,6 +115,7 @@ constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 256;
constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 4; constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 4;
#endif #endif
constexpr uint32_t kIndexBufferOffsetAlignment = 4; constexpr uint32_t kIndexBufferOffsetAlignment = 4;
constexpr uint32_t kTextureToBufferBlittingAlignment = 256;
// Front end binding limits // Front end binding limits
constexpr uint32_t kMaxGLSamplerBindings = 2 * kMaxShaderSamplers; constexpr uint32_t kMaxGLSamplerBindings = 2 * kMaxShaderSamplers;
...@@ -138,6 +139,21 @@ constexpr size_t kOcclusionQueryResultSize = sizeof(uint64_t); ...@@ -138,6 +139,21 @@ constexpr size_t kOcclusionQueryResultSize = sizeof(uint64_t);
// NOTE(hqle): Support ES 3.0. // NOTE(hqle): Support ES 3.0.
constexpr gl::Version kMaxSupportedGLVersion = gl::Version(2, 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> template <typename T>
struct ImplTypeHelper; struct ImplTypeHelper;
......
...@@ -128,6 +128,33 @@ struct IndexGenerationParams ...@@ -128,6 +128,33 @@ struct IndexGenerationParams
uint32_t dstOffset; 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 // Utils class for clear & blitting
class ClearUtils final : angle::NonCopyable class ClearUtils final : angle::NonCopyable
{ {
...@@ -353,6 +380,40 @@ class MipmapUtils final : angle::NonCopyable ...@@ -353,6 +380,40 @@ class MipmapUtils final : angle::NonCopyable
AutoObjCPtr<id<MTLComputePipelineState>> m3DMipGeneratorPipeline; 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 // RenderUtils: container class of various util classes above
class RenderUtils : public Context, angle::NonCopyable class RenderUtils : public Context, angle::NonCopyable
{ {
...@@ -411,6 +472,13 @@ class RenderUtils : public Context, angle::NonCopyable ...@@ -411,6 +472,13 @@ class RenderUtils : public Context, angle::NonCopyable
bool sRGBMipmap, bool sRGBMipmap,
gl::TexLevelArray<mtl::TextureRef> *mipmapOutputViews); 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: private:
// override ErrorHandler // override ErrorHandler
void handleError(GLenum error, void handleError(GLenum error,
...@@ -428,6 +496,7 @@ class RenderUtils : public Context, angle::NonCopyable ...@@ -428,6 +496,7 @@ class RenderUtils : public Context, angle::NonCopyable
IndexGeneratorUtils mIndexUtils; IndexGeneratorUtils mIndexUtils;
VisibilityResultUtils mVisibilityResultUtils; VisibilityResultUtils mVisibilityResultUtils;
MipmapUtils mMipmapUtils; MipmapUtils mMipmapUtils;
std::array<CopyPixelsUtils, angle::EnumSize<PixelType>()> mCopyPixelsUtils;
}; };
} // namespace mtl } // namespace mtl
......
...@@ -56,6 +56,15 @@ angle::Result ReadTexturePerSliceBytes(const gl::Context *context, ...@@ -56,6 +56,15 @@ angle::Result ReadTexturePerSliceBytes(const gl::Context *context,
uint32_t sliceOrDepth, uint32_t sliceOrDepth,
uint8_t *dataOut); 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 GetViewport(const gl::Rectangle &rect, double znear = 0, double zfar = 1);
MTLViewport GetViewportFlipY(const gl::Rectangle &rect, MTLViewport GetViewportFlipY(const gl::Rectangle &rect,
NSUInteger screenHeight, NSUInteger screenHeight,
...@@ -132,6 +141,8 @@ bool IsFormatEmulated(const mtl::Format &mtlFormat); ...@@ -132,6 +141,8 @@ bool IsFormatEmulated(const mtl::Format &mtlFormat);
// has alpha channel. // has alpha channel.
MTLClearColor EmulatedAlphaClearColor(MTLClearColor color, MTLColorWriteMask colorMask); MTLClearColor EmulatedAlphaClearColor(MTLClearColor color, MTLColorWriteMask colorMask);
gl::Box MTLRegionToGLBox(const MTLRegion &mtlRegion);
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
} // namespace mtl } // namespace mtl
} // namespace rx } // namespace rx
......
...@@ -321,6 +321,42 @@ angle::Result ReadTexturePerSliceBytes(const gl::Context *context, ...@@ -321,6 +321,42 @@ angle::Result ReadTexturePerSliceBytes(const gl::Context *context,
return angle::Result::Continue; 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 GetViewport(const gl::Rectangle &rect, double znear, double zfar)
{ {
MTLViewport re; MTLViewport re;
...@@ -783,5 +819,12 @@ MTLClearColor EmulatedAlphaClearColor(MTLClearColor color, MTLColorWriteMask col ...@@ -783,5 +819,12 @@ MTLClearColor EmulatedAlphaClearColor(MTLClearColor color, MTLColorWriteMask col
return re; 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 mtl
} // namespace rx } // namespace rx
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <array> #include <array>
#include "test_utils/gl_raii.h"
#include "util/random_utils.h" #include "util/random_utils.h"
using namespace angle; using namespace angle;
...@@ -61,21 +62,23 @@ TEST_P(ReadPixelsTest, OutOfBounds) ...@@ -61,21 +62,23 @@ TEST_P(ReadPixelsTest, OutOfBounds)
} }
} }
class ReadPixelsPBOTest : public ReadPixelsTest class ReadPixelsPBONVTest : public ReadPixelsTest
{ {
protected: protected:
ReadPixelsPBOTest() : mPBO(0), mTexture(0), mFBO(0) {} ReadPixelsPBONVTest() : mPBO(0), mTexture(0), mFBO(0) {}
void testSetUp() override void testSetUp() override
{ {
glGenBuffers(1, &mPBO); glGenBuffers(1, &mPBO);
glGenFramebuffers(1, &mFBO); 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); glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
glBufferData(GL_PIXEL_PACK_BUFFER, bufferSize, nullptr, GL_STATIC_DRAW); glBufferData(GL_PIXEL_PACK_BUFFER, bufferSize, nullptr, GL_STATIC_DRAW);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
...@@ -83,7 +86,9 @@ class ReadPixelsPBOTest : public ReadPixelsTest ...@@ -83,7 +86,9 @@ class ReadPixelsPBOTest : public ReadPixelsTest
glDeleteTextures(1, &mTexture); glDeleteTextures(1, &mTexture);
glGenTextures(1, &mTexture); glGenTextures(1, &mTexture);
glBindTexture(GL_TEXTURE_2D, 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); glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
...@@ -99,9 +104,183 @@ class ReadPixelsPBOTest : public ReadPixelsTest ...@@ -99,9 +104,183 @@ class ReadPixelsPBOTest : public ReadPixelsTest
glDeleteFramebuffers(1, &mFBO); glDeleteFramebuffers(1, &mFBO);
} }
bool hasPBOExts() const
{
return IsGLExtensionEnabled("GL_NV_pixel_buffer_object") &&
IsGLExtensionEnabled("GL_EXT_texture_storage");
}
GLuint mPBO = 0; GLuint mPBO = 0;
GLuint mTexture = 0; GLuint mTexture = 0;
GLuint mFBO = 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. // Test basic usage of PBOs.
...@@ -696,6 +875,7 @@ TEST_P(ReadPixelsErrorTest, ReadBufferIsNone) ...@@ -696,6 +875,7 @@ TEST_P(ReadPixelsErrorTest, ReadBufferIsNone)
// Use this to select which configurations (e.g. which renderer, which GLES major version) these // Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against. // tests should be run against.
ANGLE_INSTANTIATE_TEST_ES2(ReadPixelsTest); ANGLE_INSTANTIATE_TEST_ES2(ReadPixelsTest);
ANGLE_INSTANTIATE_TEST_ES2(ReadPixelsPBONVTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsPBOTest); ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsPBOTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsPBODrawTest); ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsPBODrawTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsMultisampleTest); 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