bool sampleShadingSupport() const { return fSampleShadingSupport; }
+ bool fenceSyncSupport() const { return fFenceSyncSupport; }
+
protected:
/** Subclasses must call this at the end of their constructors in order to apply caps
overrides requested by the client. Note that overrides will only reduce the caps never
bool fPreferVRAMUseOverFlushes : 1;
bool fSampleShadingSupport : 1;
+ // TODO: this may need to be an enum to support different fence types
+ bool fFenceSyncSupport : 1;
InstancedSupport fInstancedSupport;
return reinterpret_cast<T * const *>(sp);
}
+/*
+ * Object for CPU-GPU synchronization
+ */
+typedef intptr_t GrFence;
+
#endif
// OpenGL 3.1
typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTextureBufferProc)(GrGLuint texture, GrGLenum target, GrGLenum internalformat, GrGLuint buffer);
+/* ARB_sync */
+typedef GrGLsync (GR_GL_FUNCTION_TYPE* GrGLFenceSyncProc)(GrGLenum condition, GrGLbitfield flags);
+typedef GrGLenum (GR_GL_FUNCTION_TYPE* GrGLClientWaitSyncProc)(GrGLsync sync, GrGLbitfield flags, GrGLuint64 timeout);
+typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDeleteSyncProc)(GrGLsync sync);
+
/* KHR_debug */
typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDebugMessageControlProc)(GrGLenum source, GrGLenum type, GrGLenum severity, GrGLsizei count, const GrGLuint* ids, GrGLboolean enabled);
typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDebugMessageInsertProc)(GrGLenum source, GrGLenum type, GrGLuint id, GrGLenum severity, GrGLsizei length, const GrGLchar* buf);
// OpenGL 3.1
GrGLFunction<GrGLTextureBufferProc> fTextureBuffer;
+ /* ARB_sync */
+ GrGLFunction<GrGLFenceSyncProc> fFenceSync;
+ GrGLFunction<GrGLClientWaitSyncProc> fClientWaitSync;
+ GrGLFunction<GrGLDeleteSyncProc> fDeleteSync;
+
/* KHR_debug */
GrGLFunction<GrGLDebugMessageControlProc> fDebugMessageControl;
GrGLFunction<GrGLDebugMessageInsertProc> fDebugMessageInsert;
typedef signed long int GrGLsizeiptr;
#endif
typedef void* GrGLeglImage;
+typedef void* GrGLsync;
struct GrGLDrawArraysIndirectCommand {
GrGLuint fCount;
fFullClearIsFree = false;
fMustClearUploadedBufferData = false;
fSampleShadingSupport = false;
+ fFenceSyncSupport = false;
fUseDrawInsteadOfClear = false;
r.appendf("Prefer client-side dynamic buffers : %s\n", gNY[fPreferClientSideDynamicBuffers]);
r.appendf("Full screen clear is free : %s\n", gNY[fFullClearIsFree]);
r.appendf("Must clear buffer memory : %s\n", gNY[fMustClearUploadedBufferData]);
+ r.appendf("Sample shading support : %s\n", gNY[fSampleShadingSupport]);
+ r.appendf("Fence sync support : %s\n", gNY[fFenceSyncSupport]);
+
r.appendf("Draw Instead of Clear [workaround] : %s\n", gNY[fUseDrawInsteadOfClear]);
r.appendf("Draw Instead of TexSubImage [workaround] : %s\n",
gNY[fUseDrawInsteadOfPartialRenderTargetWrite]);
bool GrGpu::transferPixels(GrSurface* surface,
int left, int top, int width, int height,
GrPixelConfig config, GrBuffer* transferBuffer,
- size_t offset, size_t rowBytes) {
+ size_t offset, size_t rowBytes, GrFence* fence) {
SkASSERT(transferBuffer);
+ SkASSERT(fence);
this->handleDirtyContext();
if (this->onTransferPixels(surface, left, top, width, height, config,
SkIRect rect = SkIRect::MakeXYWH(left, top, width, height);
this->didWriteToSurface(surface, &rect);
fStats.incTransfersToTexture();
+
+ if (*fence) {
+ this->deleteFence(*fence);
+ }
+ *fence = this->insertFence();
+
return true;
}
return false;
bool transferPixels(GrSurface* surface,
int left, int top, int width, int height,
GrPixelConfig config, GrBuffer* transferBuffer,
- size_t offset, size_t rowBytes);
+ size_t offset, size_t rowBytes, GrFence* fence);
/**
* This is can be called before allocating a texture to be a dst for copySurface. This is only
// Provides a hook for post-flush actions (e.g. PLS reset and Vulkan command buffer submits).
virtual void finishDrawTarget() {}
+ virtual GrFence SK_WARN_UNUSED_RESULT insertFence() const = 0;
+ virtual bool waitFence(GrFence, uint64_t timeout = 1000) const = 0;
+ virtual void deleteFence(GrFence) const = 0;
+
///////////////////////////////////////////////////////////////////////////
// Debugging and Stats
GET_EGL_PROC_SUFFIX(DestroyImage, KHR);
}
- if (glVer >= GR_GL_VER(4,0) || extensions.has("GL_ARB_sample_shading")) {
+ if (glVer >= GR_GL_VER(4, 0) || extensions.has("GL_ARB_sample_shading")) {
GET_PROC(MinSampleShading);
}
+ if (glVer >= GR_GL_VER(3, 2) || extensions.has("GL_ARB_sync")) {
+ GET_PROC(FenceSync);
+ GET_PROC(ClientWaitSync);
+ GET_PROC(DeleteSync);
+ }
+
interface->fStandard = kGL_GrGLStandard;
interface->fExtensions.swap(&extensions);
GET_PROC_SUFFIX(MinSampleShading, OES);
}
+ if (version >= GR_GL_VER(3, 0)) {
+ GET_PROC(FenceSync);
+ GET_PROC(ClientWaitSync);
+ GET_PROC(DeleteSync);
+ }
+
interface->fStandard = kGLES_GrGLStandard;
interface->fExtensions.swap(&extensions);
fSampleShadingSupport = true;
}
+ // TODO: support CHROMIUM_sync_point and maybe KHR_fence_sync
+ if (kGL_GrGLStandard == standard) {
+ if (version >= GR_GL_VER(3, 2) || ctxInfo.hasExtension("GL_ARB_sync")) {
+ fFenceSyncSupport = true;
+ }
+ } else if (version >= GR_GL_VER(3, 0)) {
+ fFenceSyncSupport = true;
+ }
+
// We support manual mip-map generation (via iterative downsampling draw calls). This fixes
// bugs on some cards/drivers that produce incorrect mip-maps for sRGB textures when using
// glGenerateMipmap. Our implementation requires mip-level sampling control. Additionally,
#define GR_GL_INCLUSIVE 0x8f10
#define GR_GL_EXCLUSIVE 0x8f11
+/* GL_ARB_sync */
+#define GR_GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
+#define GR_GL_ALREADY_SIGNALED 0x911A
+#define GR_GL_TIMEOUT_EXPIRED 0x911B
+#define GR_GL_CONDITION_SATISFIED 0x911C
+#define GR_GL_WAIT_FAILED 0x911D
+#define GR_GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001
+
/* EGL Defines */
#define GR_EGL_NO_DISPLAY ((GrEGLDisplay)0)
#define GR_EGL_EXTENSIONS 0x3055
}
return false;
}
+
+GrFence SK_WARN_UNUSED_RESULT GrGLGpu::insertFence() const {
+ GrGLsync fence;
+ GL_CALL_RET(fence, FenceSync(GR_GL_SYNC_GPU_COMMANDS_COMPLETE, 0));
+ return (GrFence)fence;
+}
+
+bool GrGLGpu::waitFence(GrFence fence, uint64_t timeout) const {
+ GrGLenum result;
+ GL_CALL_RET(result, ClientWaitSync((GrGLsync)fence, GR_GL_SYNC_FLUSH_COMMANDS_BIT, timeout));
+ return (GR_GL_CONDITION_SATISFIED == result);
+}
+
+void GrGLGpu::deleteFence(GrFence fence) const {
+ GL_CALL(DeleteSync((GrGLsync)fence));
+}
void finishDrawTarget() override;
+ GrFence SK_WARN_UNUSED_RESULT insertFence() const override;
+ bool waitFence(GrFence, uint64_t timeout) const override;
+ void deleteFence(GrFence) const override;
+
private:
GrGLGpu(GrGLContext* ctx, GrContext* context);
}
}
+ if (kGL_GrGLStandard == fStandard) {
+ if (glVer >= GR_GL_VER(3, 2) || fExtensions.has("GL_ARB_sync")) {
+ if (nullptr == fFunctions.fFenceSync ||
+ nullptr == fFunctions.fClientWaitSync ||
+ nullptr == fFunctions.fDeleteSync) {
+ RETURN_FALSE_INTERFACE
+ }
+ }
+ } else if (kGLES_GrGLStandard == fStandard) {
+ if (glVer >= GR_GL_VER(3, 0)) {
+ if (nullptr == fFunctions.fFenceSync ||
+ nullptr == fFunctions.fClientWaitSync ||
+ nullptr == fFunctions.fDeleteSync) {
+ RETURN_FALSE_INTERFACE
+ }
+ }
+ }
+
if (fExtensions.has("EGL_KHR_image") || fExtensions.has("EGL_KHR_image_base")) {
if (nullptr == fFunctions.fEGLCreateImage ||
nullptr == fFunctions.fEGLDestroyImage) {
fFunctions.fMapNamedBufferRange = bind_to_member(this, &GrGLTestInterface::mapNamedBufferRange);
fFunctions.fFlushMappedNamedBufferRange = bind_to_member(this, &GrGLTestInterface::flushMappedNamedBufferRange);
fFunctions.fTextureBuffer = bind_to_member(this, &GrGLTestInterface::textureBuffer);
+ fFunctions.fFenceSync = bind_to_member(this, &GrGLTestInterface::fenceSync);
+ fFunctions.fClientWaitSync = bind_to_member(this, &GrGLTestInterface::clientWaitSync);
+ fFunctions.fDeleteSync = bind_to_member(this, &GrGLTestInterface::deleteSync);
fFunctions.fDebugMessageControl = bind_to_member(this, &GrGLTestInterface::debugMessageControl);
fFunctions.fDebugMessageInsert = bind_to_member(this, &GrGLTestInterface::debugMessageInsert);
fFunctions.fDebugMessageCallback = bind_to_member(this, &GrGLTestInterface::debugMessageCallback);
virtual GrGLvoid* mapNamedBufferRange(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr length, GrGLbitfield access) { return nullptr; }
virtual GrGLvoid flushMappedNamedBufferRange(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr length) {}
virtual GrGLvoid textureBuffer(GrGLuint texture, GrGLenum target, GrGLenum internalformat, GrGLuint buffer) {}
+ virtual GrGLsync fenceSync(GrGLenum condition, GrGLbitfield flags) { return nullptr; }
+ virtual GrGLenum clientWaitSync(GrGLsync sync, GrGLbitfield flags, GrGLuint64 timeout) { return GR_GL_WAIT_FAILED; }
+ virtual GrGLvoid deleteSync(GrGLsync sync) {}
virtual GrGLvoid debugMessageControl(GrGLenum source, GrGLenum type, GrGLenum severity, GrGLsizei count, const GrGLuint* ids, GrGLboolean enabled) {}
virtual GrGLvoid debugMessageInsert(GrGLenum source, GrGLenum type, GrGLuint id, GrGLenum severity, GrGLsizei length, const GrGLchar* buf) {}
virtual GrGLvoid debugMessageCallback(GRGLDEBUGPROC callback, const GrGLvoid* userParam) {}
fOversizedStencilSupport = false; //TODO: figure this out
fUseDrawInsteadOfClear = false;
+ fFenceSyncSupport = true; // always available in Vulkan
fMapBufferFlags = kNone_MapFlags; //TODO: figure this out
fBufferMapThreshold = SK_MaxS32; //TODO: figure this out
this->didWriteToSurface(target, &bounds);
}
+GrFence SK_WARN_UNUSED_RESULT GrVkGpu::insertFence() const {
+ VkFenceCreateInfo createInfo;
+ memset(&createInfo, 0, sizeof(VkFenceCreateInfo));
+ createInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+ createInfo.pNext = nullptr;
+ createInfo.flags = 0;
+ VkFence fence = VK_NULL_HANDLE;
+ VkResult result = GR_VK_CALL(this->vkInterface(), CreateFence(this->device(), &createInfo,
+ nullptr, &fence));
+ // TODO: verify that all QueueSubmits before this will finish before this fence signals
+ if (VK_SUCCESS == result) {
+ GR_VK_CALL(this->vkInterface(), QueueSubmit(this->queue(), 0, nullptr, fence));
+ }
+ return (GrFence)fence;
+}
+
+bool GrVkGpu::waitFence(GrFence fence, uint64_t timeout) const {
+ VkResult result = GR_VK_CALL(this->vkInterface(), WaitForFences(this->device(), 1,
+ (VkFence*)&fence,
+ VK_TRUE,
+ timeout));
+ return (VK_SUCCESS == result);
+}
+
+void GrVkGpu::deleteFence(GrFence fence) const {
+ GR_VK_CALL(this->vkInterface(), DestroyFence(this->device(), (VkFence)fence, nullptr));
+}
+
void finishDrawTarget() override;
+ GrFence SK_WARN_UNUSED_RESULT insertFence() const override;
+ bool waitFence(GrFence, uint64_t timeout) const override;
+ void deleteFence(GrFence) const override;
+
void generateMipmap(GrVkTexture* tex);
bool updateBuffer(GrVkBuffer* buffer, const void* src, VkDeviceSize offset, VkDeviceSize size);
void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override {}
+ GrFence SK_WARN_UNUSED_RESULT insertFence() const override { return 0; }
+ bool waitFence(GrFence, uint64_t) const override { return true; }
+ void deleteFence(GrFence) const override {}
+
private:
void onResetContext(uint32_t resetBits) override {}