Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow SkImage::asyncRescaleAndReadPixels() to work async on WebGL #169

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
allow SkImage::asyncRescaleAndReadPixels() to work async on WebGL
Reading back pixels is blocking on WebGL. This is because skia lacks pixel
transfer support with out mapping.

WebGL has PBO for async reading, however it use `glGetBufferSubData()` to read
back from PIXEL_PACK_BUFFER instead of mapping

- add GetBufferSubData to GrGLInterface
- add GrGpuBuffer::getData()
- TAsyncReadResult use getData() to copy pixels to cpu plane
jamesruan committed Mar 28, 2024
commit 3036904b537e48dbe04b2e4fef5d957536294ad1
1 change: 1 addition & 0 deletions include/gpu/gl/GrGLFunctions.h
Original file line number Diff line number Diff line change
@@ -95,6 +95,7 @@ using GrGLGenSamplersFn = GrGLvoid GR_GL_FUNCTION_TYPE(GrGLsizei count, GrGLuint
using GrGLGenTexturesFn = GrGLvoid GR_GL_FUNCTION_TYPE(GrGLsizei n, GrGLuint* textures);
using GrGLGenVertexArraysFn = GrGLvoid GR_GL_FUNCTION_TYPE(GrGLsizei n, GrGLuint* arrays);
using GrGLGetBufferParameterivFn = GrGLvoid GR_GL_FUNCTION_TYPE(GrGLenum target, GrGLenum pname, GrGLint* params);
using GrGLGetBufferSubDataFn = GrGLvoid GR_GL_FUNCTION_TYPE(GrGLenum target, GrGLintptr offset, GrGLsizeiptr size, GrGLvoid* data);
using GrGLGetErrorFn = GrGLenum GR_GL_FUNCTION_TYPE();
using GrGLGetFramebufferAttachmentParameterivFn = GrGLvoid GR_GL_FUNCTION_TYPE(GrGLenum target, GrGLenum attachment, GrGLenum pname, GrGLint* params);
using GrGLGetFloatvFn = GrGLvoid GR_GL_FUNCTION_TYPE(GrGLenum pname, GrGLfloat* params);
1 change: 1 addition & 0 deletions include/gpu/gl/GrGLInterface.h
Original file line number Diff line number Diff line change
@@ -165,6 +165,7 @@ struct SK_API GrGLInterface : public SkRefCnt {
GrGLFunction<GrGLGenTexturesFn> fGenTextures;
GrGLFunction<GrGLGenVertexArraysFn> fGenVertexArrays;
GrGLFunction<GrGLGetBufferParameterivFn> fGetBufferParameteriv;
GrGLFunction<GrGLGetBufferSubDataFn> fGetBufferSubData;
GrGLFunction<GrGLGetErrorFn> fGetError;
GrGLFunction<GrGLGetFramebufferAttachmentParameterivFn> fGetFramebufferAttachmentParameteriv;
GrGLFunction<GrGLGetFloatvFn> fGetFloatv;
15 changes: 13 additions & 2 deletions src/gpu/AsyncReadTypes.h
Original file line number Diff line number Diff line change
@@ -153,11 +153,22 @@ class TAsyncReadResult : public SkImage::AsyncReadResult {
size_t rowBytes,
TClientMappedBufferManager<T, IDType>* manager) {
const void* mappedData = result.fTransferBuffer->map();
size_t size = rowBytes*dimensions.height();
if (!mappedData) {
return false;
sk_sp<SkData> data = SkData::MakeUninitialized(size);
if (!result.fTransferBuffer->getData(data->writable_data(), 0, size)) {
return false;
}
if (result.fPixelConverter) {
sk_sp<SkData> out = SkData::MakeUninitialized(size);
result.fPixelConverter(out->writable_data(), data->data());
this->addCpuPlane(std::move(out), rowBytes);
} else {
this->addCpuPlane(std::move(data), rowBytes);
}
return true;
}
if (result.fPixelConverter) {
size_t size = rowBytes*dimensions.height();
sk_sp<SkData> data = SkData::MakeUninitialized(size);
result.fPixelConverter(data->writable_data(), mappedData);
this->addCpuPlane(std::move(data), rowBytes);
4 changes: 4 additions & 0 deletions src/gpu/ganesh/GrGpuBuffer.cpp
Original file line number Diff line number Diff line change
@@ -83,6 +83,10 @@ bool GrGpuBuffer::updateData(const void* src, size_t offset, size_t size, bool p
return this->onUpdateData(src, offset, size, preserve);
}

bool GrGpuBuffer::getData(void* dst, size_t offset, size_t size) {
return this->onGetData(dst, offset, size);
}

void GrGpuBuffer::ComputeScratchKeyForDynamicBuffer(size_t size,
GrGpuBufferType intendedType,
skgpu::ScratchKey* key) {
10 changes: 10 additions & 0 deletions src/gpu/ganesh/GrGpuBuffer.h
Original file line number Diff line number Diff line change
@@ -96,6 +96,15 @@ class GrGpuBuffer : public GrGpuResource, public GrBuffer {
*/
bool updateData(const void* src, size_t offset, size_t size, bool preserve);

/**
* Get the buffer data.
*
* WebGL's buffer can't be mapped to client side. Use this function to get the buffer data.
*
* @return returns true if the update succeeds, false otherwise.
*/
bool getData(void* dst, size_t offset, size_t size);

GrGpuBufferType intendedType() const { return fIntendedType; }

protected:
@@ -129,6 +138,7 @@ class GrGpuBuffer : public GrGpuResource, public GrBuffer {
virtual void onUnmap(MapType) = 0;
virtual bool onClearToZero() = 0;
virtual bool onUpdateData(const void* src, size_t offset, size_t size, bool preserve) = 0;
virtual bool onGetData(void* dst, size_t offset, size_t size) { return false; }

size_t onGpuMemorySize() const override { return fSizeInBytes; }
void onSetLabel() override{}
1 change: 1 addition & 0 deletions src/gpu/ganesh/gl/GrGLAssembleGLInterfaceAutogen.cpp
Original file line number Diff line number Diff line change
@@ -93,6 +93,7 @@ sk_sp<const GrGLInterface> GrGLMakeAssembledGLInterface(void *ctx, GrGLGetProc g
GET_PROC(GenBuffers);
GET_PROC(GenTextures);
GET_PROC(GetBufferParameteriv);
GET_PROC(GetBufferSubData);
GET_PROC(GetError);
GET_PROC(GetFloatv);
GET_PROC(GetIntegerv);
1 change: 1 addition & 0 deletions src/gpu/ganesh/gl/GrGLAssembleWebGLInterfaceAutogen.cpp
Original file line number Diff line number Diff line change
@@ -215,6 +215,7 @@ sk_sp<const GrGLInterface> GrGLMakeAssembledWebGLInterface(void *ctx, GrGLGetPro

if (glVer >= GR_GL_VER(2,0)) {
GET_PROC(CopyBufferSubData);
GET_PROC(GetBufferSubData);
}

if (glVer >= GR_GL_VER(2,0)) {
12 changes: 12 additions & 0 deletions src/gpu/ganesh/gl/GrGLBuffer.cpp
Original file line number Diff line number Diff line change
@@ -275,6 +275,18 @@ bool GrGLBuffer::onUpdateData(const void* src, size_t offset, size_t size, bool
return true;
}

bool GrGLBuffer::onGetData(void* dst, size_t offset, size_t size) {
SkASSERT(fBufferID);

// bindbuffer handles dirty context
GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
if (this->glCaps().getBufferSubDataSupport()) {
GL_CALL(GetBufferSubData(target, offset, size, dst));
return true;
}
return false;
}

void GrGLBuffer::onSetLabel() {
SkASSERT(fBufferID);
if (!this->getLabel().empty()) {
1 change: 1 addition & 0 deletions src/gpu/ganesh/gl/GrGLBuffer.h
Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@ class GrGLBuffer : public GrGpuBuffer {
void onUnmap(MapType) override;
bool onClearToZero() override;
bool onUpdateData(const void* src, size_t offset, size_t size, bool preserve) override;
bool onGetData(void* dst, size_t offset, size_t size) override;

void onSetLabel() override;

10 changes: 10 additions & 0 deletions src/gpu/ganesh/gl/GrGLCaps.cpp
Original file line number Diff line number Diff line change
@@ -100,6 +100,7 @@ GrGLCaps::GrGLCaps(const GrContextOptions& contextOptions,
fFBFetchRequiresEnablePerSample = false;
fSRGBWriteControl = false;
fSkipErrorChecks = false;
fGetBufferSubDataSupport = false;

fShaderCaps = std::make_unique<GrShaderCaps>();

@@ -400,6 +401,10 @@ void GrGLCaps::init(const GrContextOptions& contextOptions,
return SkToBool(contextFlags & GR_GL_CONTEXT_FLAG_PROTECTED_CONTENT_BIT_EXT);
}();

if (GR_IS_GR_GL(standard) || GR_IS_GR_WEBGL(standard)) {
fGetBufferSubDataSupport = version >= GR_GL_VER(2, 0);
}

/**************************************************************************
* GrShaderCaps fields
**************************************************************************/
@@ -557,6 +562,11 @@ void GrGLCaps::init(const GrContextOptions& contextOptions,
} else if (GR_IS_GR_WEBGL(standard)) {
// explicitly removed https://www.khronos.org/registry/webgl/specs/2.0/#5.14
fMapBufferFlags = kNone_MapFlags;
if (version >= GR_GL_VER(2, 0)) {
fTransferFromBufferToTextureSupport = true;
fTransferFromSurfaceToBufferSupport = true;
fTransferBufferType = TransferBufferType::kARB_PBO;
}
}

// Buffers have more restrictions in WebGL than GLES. For example,
3 changes: 3 additions & 0 deletions src/gpu/ganesh/gl/GrGLCaps.h
Original file line number Diff line number Diff line change
@@ -515,6 +515,8 @@ class GrGLCaps : public GrCaps {

bool clientCanDisableMultisample() const { return fClientCanDisableMultisample; }

bool getBufferSubDataSupport() const { return fGetBufferSubDataSupport; }

GrBackendFormat getBackendFormatFromCompressionType(SkTextureCompressionType) const override;

skgpu::Swizzle getWriteSwizzle(const GrBackendFormat&, GrColorType) const override;
@@ -630,6 +632,7 @@ class GrGLCaps : public GrCaps {
bool fSRGBWriteControl : 1;
bool fSkipErrorChecks : 1;
bool fClientCanDisableMultisample : 1;
bool fGetBufferSubDataSupport: 1;

// Driver workarounds
bool fDoManualMipmapping : 1;