Commit 5d9f5df0 by Stanislav Chiknavaryan Committed by Commit Bot

Implementation of eglGetSyncValuesCHROMIUM extension.

This change adds implementation of eglGetSyncValuesCHROMIUM extension on D3D11 with Direct Composition. This should work on Windows 8.1 and above. The implementation is based on IDXGISwapChain::GetFrameStatistics. Extension documentation: https://chromium.googlesource.com/chromium/src/gpu/+/master/GLES2/extensions/CHROMIUM/EGL_CHROMIUM_get_sync_values.txt BUG=angleproject:1402 Change-Id: I306434dd8d85d618b14edfa38fc2a22e50fddacc Reviewed-on: https://chromium-review.googlesource.com/390351 Commit-Queue: Stanislav Chiknavaryan <stanisc@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 873d00f4
Name
CHROMIUM_get_sync_values
Name Strings
EGL_CHROMIUM_get_sync_values
Contact
Stéphane Marchesin, Google (marcheu 'at' google.com)
Status
Draft.
Version
Last Modified Date: N/A Revision: 1.0
Based on GLX_OML_sync_control Revision 6.0
Number
???
Dependencies
The extension is written against the EGL 1.2 Specification, although it
should work on other versions of these specifications. This extension
also requires an operating system which supports CLOCK_MONOTONIC.
Overview
This extension provides counters which let applications know about the
timing of the last vertical retrace. By looking at the system clock, as
well as the refresh rate of the monitor, this should enable applications
to predict the position of future retraces so as to schedule an optimal
workload.
This extension incorporates the use of three counters that provide
the necessary synchronization. The Unadjusted System Time (or UST)
is the 64-bit CLOCK_MONOTONIC clock; in particular this lets the
application schedule future vertical retraces by querying this clock.
The graphics Media Stream Counter (or graphics MSC) is a counter
that is unique to the graphics subsystem and increments for each
vertical retrace that occurs. The Swap Buffer Counter (SBC) is an
attribute of an EGLSurface and is incremented each time a swap
buffer action is performed on the associated surface.
The use of these three counters allows the application to
synchronize graphics rendering to vertical retraces and/or swap
buffer actions. For example, by querying the synchronization values for
a given surface, the application can accurately predict the timing for
the next vertical retraces and schedule rendering accordingly.
Issues
None.
IP Status
No known issues.
New Procedures and Functions
Bool eglGetSyncValuesCHROMIUM(EGLDisplay dpy,
EGLSurface surface,
int64_t* ust,
int64_t* msc,
int64_t* sbc)
New Tokens
None
Additions to the EGL 1.3 Specification
eglGetSyncValuesCHROMIUM returns the current UST/MSC/SBC triple. A UST
timestamp is obtained each time the graphics MSC is incremented.
If this value does not reflect the value of the UST at the time the
first scan line of the display begins passing through the video
output port, it will be adjusted by the graphics driver to do so
prior to being returned by any of the functions defined by this
extension.
This UST timestamp, together with the current graphics MSC and the
current SBC, comprise the current UST/MSC/SBC triple. The UST,
graphics MSC, and SBC values are not part of the render context
state. These values cannot be pushed or popped. The graphics MSC
value is initialized to 0 when the graphics device is initialized.
The SBC is per-surface state and is initialized to 0 when the
EGLSurface data structure is initialized.
The SBC value is incremented by the graphics driver at the completion
of each buffer swap (e.g., the pixel copy has been completed or the
hardware register that swaps memory banks has been written). For pixel
formats that do not contain a back buffer, the SBC will always be
returned as 0.
The graphics MSC value is incremented once for each screen refresh.
For a non-interlaced display, this means that the graphics MSC value
is incremented for each frame. For an interlaced display, it means
that it will be incremented for each field. For a multi-monitor
system, the monitor used to determine MSC is the one where the surface
is located. If the surface spans multiple monitors, the monitor used
to determine MSC is the one with the biggest coverage in pixels.
The function eglGetSyncValuesCHROMIUM will return TRUE if the function
completed successfully, FALSE otherwise.
Each time eglSwapBuffer succeeds, the SBC will be increased within a
finite time period.
Errors
eglGetSyncValuesCHROMIUM will return FALSE if there is no current
EGLContext.
New State
Get Value Get Command Type Initial Value
--------- ----------- ---- -------------
[UST] eglGetSyncValuesCHROMIUM Z unspecified
[MSC] eglGetSyncValuesCHROMIUM Z 0
[SBC] eglGetSyncValuesCHROMIUM Z 0
New Implementation Dependent State
None
...@@ -782,7 +782,8 @@ DisplayExtensions::DisplayExtensions() ...@@ -782,7 +782,8 @@ DisplayExtensions::DisplayExtensions()
streamConsumerGLTextureYUV(false), streamConsumerGLTextureYUV(false),
streamProducerD3DTextureNV12(false), streamProducerD3DTextureNV12(false),
createContextWebGLCompatibility(false), createContextWebGLCompatibility(false),
createContextBindGeneratesResource(false) createContextBindGeneratesResource(false),
getSyncValues(false)
{ {
} }
...@@ -818,6 +819,7 @@ std::vector<std::string> DisplayExtensions::getStrings() const ...@@ -818,6 +819,7 @@ std::vector<std::string> DisplayExtensions::getStrings() const
InsertExtensionString("EGL_ANGLE_stream_producer_d3d_texture_nv12", streamProducerD3DTextureNV12, &extensionStrings); InsertExtensionString("EGL_ANGLE_stream_producer_d3d_texture_nv12", streamProducerD3DTextureNV12, &extensionStrings);
InsertExtensionString("EGL_ANGLE_create_context_webgl_compatibility", createContextWebGLCompatibility, &extensionStrings); InsertExtensionString("EGL_ANGLE_create_context_webgl_compatibility", createContextWebGLCompatibility, &extensionStrings);
InsertExtensionString("EGL_CHROMIUM_create_context_bind_generates_resource", createContextBindGeneratesResource, &extensionStrings); InsertExtensionString("EGL_CHROMIUM_create_context_bind_generates_resource", createContextBindGeneratesResource, &extensionStrings);
InsertExtensionString("EGL_CHROMIUM_sync_control", getSyncValues, &extensionStrings);
// TODO(jmadill): Enable this when complete. // TODO(jmadill): Enable this when complete.
//InsertExtensionString("KHR_create_context_no_error", createContextNoError, &extensionStrings); //InsertExtensionString("KHR_create_context_no_error", createContextNoError, &extensionStrings);
// clang-format on // clang-format on
......
...@@ -601,6 +601,9 @@ struct DisplayExtensions ...@@ -601,6 +601,9 @@ struct DisplayExtensions
// EGL_CHROMIUM_create_context_bind_generates_resource // EGL_CHROMIUM_create_context_bind_generates_resource
bool createContextBindGeneratesResource; bool createContextBindGeneratesResource;
// EGL_CHROMIUM_get_sync_values
bool getSyncValues;
}; };
struct DeviceExtensions struct DeviceExtensions
......
...@@ -221,6 +221,11 @@ Error Surface::releaseTexImage(EGLint buffer) ...@@ -221,6 +221,11 @@ Error Surface::releaseTexImage(EGLint buffer)
return mImplementation->releaseTexImage(buffer); return mImplementation->releaseTexImage(buffer);
} }
Error Surface::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
{
return mImplementation->getSyncValues(ust, msc, sbc);
}
void Surface::releaseTexImageFromTexture() void Surface::releaseTexImageFromTexture()
{ {
ASSERT(mTexture.get()); ASSERT(mTexture.get());
......
...@@ -60,6 +60,8 @@ class Surface : public gl::FramebufferAttachmentObject ...@@ -60,6 +60,8 @@ class Surface : public gl::FramebufferAttachmentObject
Error bindTexImage(gl::Texture *texture, EGLint buffer); Error bindTexImage(gl::Texture *texture, EGLint buffer);
Error releaseTexImage(EGLint buffer); Error releaseTexImage(EGLint buffer);
Error getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc);
EGLint isPostSubBufferSupported() const; EGLint isPostSubBufferSupported() const;
void setSwapInterval(EGLint interval); void setSwapInterval(EGLint interval);
......
...@@ -35,6 +35,7 @@ class MockSurfaceImpl : public rx::SurfaceImpl ...@@ -35,6 +35,7 @@ class MockSurfaceImpl : public rx::SurfaceImpl
MOCK_METHOD2(querySurfacePointerANGLE, egl::Error(EGLint, void**)); MOCK_METHOD2(querySurfacePointerANGLE, egl::Error(EGLint, void**));
MOCK_METHOD2(bindTexImage, egl::Error(gl::Texture*, EGLint)); MOCK_METHOD2(bindTexImage, egl::Error(gl::Texture*, EGLint));
MOCK_METHOD1(releaseTexImage, egl::Error(EGLint)); MOCK_METHOD1(releaseTexImage, egl::Error(EGLint));
MOCK_METHOD3(getSyncValues, egl::Error(EGLuint64KHR *, EGLuint64KHR *, EGLuint64KHR *));
MOCK_METHOD1(setSwapInterval, void(EGLint)); MOCK_METHOD1(setSwapInterval, void(EGLint));
MOCK_CONST_METHOD0(getWidth, EGLint()); MOCK_CONST_METHOD0(getWidth, EGLint());
MOCK_CONST_METHOD0(getHeight, EGLint()); MOCK_CONST_METHOD0(getHeight, EGLint());
......
...@@ -9,6 +9,9 @@ ...@@ -9,6 +9,9 @@
#ifndef LIBANGLE_RENDERER_SURFACEIMPL_H_ #ifndef LIBANGLE_RENDERER_SURFACEIMPL_H_
#define LIBANGLE_RENDERER_SURFACEIMPL_H_ #define LIBANGLE_RENDERER_SURFACEIMPL_H_
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "common/angleutils.h" #include "common/angleutils.h"
#include "libANGLE/Error.h" #include "libANGLE/Error.h"
#include "libANGLE/FramebufferAttachment.h" #include "libANGLE/FramebufferAttachment.h"
...@@ -43,6 +46,7 @@ class SurfaceImpl : public FramebufferAttachmentObjectImpl ...@@ -43,6 +46,7 @@ class SurfaceImpl : public FramebufferAttachmentObjectImpl
virtual egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) = 0; virtual egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) = 0;
virtual egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) = 0; virtual egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) = 0;
virtual egl::Error releaseTexImage(EGLint buffer) = 0; virtual egl::Error releaseTexImage(EGLint buffer) = 0;
virtual egl::Error getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) = 0;
virtual void setSwapInterval(EGLint interval) = 0; virtual void setSwapInterval(EGLint interval) = 0;
// width and height can change with client window resizing // width and height can change with client window resizing
......
...@@ -95,6 +95,11 @@ egl::Error SurfaceD3D::releaseTexImage(EGLint) ...@@ -95,6 +95,11 @@ egl::Error SurfaceD3D::releaseTexImage(EGLint)
return egl::Error(EGL_SUCCESS); return egl::Error(EGL_SUCCESS);
} }
egl::Error SurfaceD3D::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
{
return mSwapChain->getSyncValues(ust, msc, sbc);
}
egl::Error SurfaceD3D::resetSwapChain() egl::Error SurfaceD3D::resetSwapChain()
{ {
ASSERT(!mSwapChain); ASSERT(!mSwapChain);
......
...@@ -36,6 +36,7 @@ class SurfaceD3D : public SurfaceImpl ...@@ -36,6 +36,7 @@ class SurfaceD3D : public SurfaceImpl
egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override; egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override;
egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override; egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override;
egl::Error releaseTexImage(EGLint buffer) override; egl::Error releaseTexImage(EGLint buffer) override;
egl::Error getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) override;
void setSwapInterval(EGLint interval) override; void setSwapInterval(EGLint interval) override;
EGLint getWidth() const override; EGLint getWidth() const override;
......
...@@ -12,9 +12,11 @@ ...@@ -12,9 +12,11 @@
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
#include <EGL/egl.h> #include <EGL/egl.h>
#include <EGL/eglext.h>
#include "common/angleutils.h" #include "common/angleutils.h"
#include "common/platform.h" #include "common/platform.h"
#include "libANGLE/Error.h"
#if !defined(ANGLE_FORCE_VSYNC_OFF) #if !defined(ANGLE_FORCE_VSYNC_OFF)
#define ANGLE_FORCE_VSYNC_OFF 0 #define ANGLE_FORCE_VSYNC_OFF 0
...@@ -50,6 +52,8 @@ class SwapChainD3D : angle::NonCopyable ...@@ -50,6 +52,8 @@ class SwapChainD3D : angle::NonCopyable
HANDLE getShareHandle() { return mShareHandle; } HANDLE getShareHandle() { return mShareHandle; }
virtual void *getKeyedMutex() = 0; virtual void *getKeyedMutex() = 0;
virtual egl::Error getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) = 0;
protected: protected:
const GLenum mOffscreenRenderTargetFormat; const GLenum mOffscreenRenderTargetFormat;
const GLenum mDepthBufferFormat; const GLenum mDepthBufferFormat;
......
...@@ -1108,6 +1108,9 @@ void Renderer11::generateDisplayExtensions(egl::DisplayExtensions *outExtensions ...@@ -1108,6 +1108,9 @@ void Renderer11::generateDisplayExtensions(egl::DisplayExtensions *outExtensions
outExtensions->flexibleSurfaceCompatibility = true; outExtensions->flexibleSurfaceCompatibility = true;
outExtensions->directComposition = !!mDCompModule; outExtensions->directComposition = !!mDCompModule;
// getSyncValues requires direct composition.
outExtensions->getSyncValues = outExtensions->directComposition;
} }
gl::Error Renderer11::flush() gl::Error Renderer11::flush()
......
...@@ -33,6 +33,12 @@ namespace rx ...@@ -33,6 +33,12 @@ namespace rx
namespace namespace
{ {
// To avoid overflow in QPC to Microseconds calculations, since we multiply
// by kMicrosecondsPerSecond, then the QPC value should not exceed
// (2^63 - 1) / 1E6. If it exceeds that threshold, we divide then multiply.
static constexpr int64_t kQPCOverflowThreshold = 0x8637BD05AF7;
static constexpr int64_t kMicrosecondsPerSecond = 1000000;
bool NeedsOffscreenTexture(Renderer11 *renderer, NativeWindow11 *nativeWindow, EGLint orientation) bool NeedsOffscreenTexture(Renderer11 *renderer, NativeWindow11 *nativeWindow, EGLint orientation)
{ {
// We don't need an offscreen texture if either orientation = INVERT_Y, // We don't need an offscreen texture if either orientation = INVERT_Y,
...@@ -82,6 +88,14 @@ SwapChain11::SwapChain11(Renderer11 *renderer, ...@@ -82,6 +88,14 @@ SwapChain11::SwapChain11(Renderer11 *renderer,
{ {
// Sanity check that if present path fast is active then we're using the default orientation // Sanity check that if present path fast is active then we're using the default orientation
ASSERT(!mRenderer->presentPathFastEnabled() || orientation == 0); ASSERT(!mRenderer->presentPathFastEnabled() || orientation == 0);
// Get the performance counter
LARGE_INTEGER counterFreqency = {};
BOOL success = QueryPerformanceFrequency(&counterFreqency);
UNUSED_ASSERTION_VARIABLE(success);
ASSERT(success);
mQPCFrequency = counterFreqency.QuadPart;
} }
SwapChain11::~SwapChain11() SwapChain11::~SwapChain11()
...@@ -877,4 +891,41 @@ void SwapChain11::recreate() ...@@ -877,4 +891,41 @@ void SwapChain11::recreate()
// possibly should use this method instead of reset // possibly should use this method instead of reset
} }
egl::Error SwapChain11::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
{
DXGI_FRAME_STATISTICS stats = {};
HRESULT result = mSwapChain->GetFrameStatistics(&stats);
if (FAILED(result))
{
return egl::Error(EGL_BAD_ALLOC, "Failed to get frame statistics, result: 0x%X", result);
}
// Conversion from DXGI_FRAME_STATISTICS to the output values:
// stats.SyncRefreshCount -> msc
// stats.PresentCount -> sbc
// stats.SyncQPCTime -> ust with conversion to microseconds via QueryPerformanceFrequency
*msc = stats.SyncRefreshCount;
*sbc = stats.PresentCount;
LONGLONG syncQPCValue = stats.SyncQPCTime.QuadPart;
// If the QPC Value is below the overflow threshold, we proceed with
// simple multiply and divide.
if (syncQPCValue < kQPCOverflowThreshold)
{
*ust = syncQPCValue * kMicrosecondsPerSecond / mQPCFrequency;
}
else
{
// Otherwise, calculate microseconds in a round about manner to avoid
// overflow and precision issues.
int64_t wholeSeconds = syncQPCValue / mQPCFrequency;
int64_t leftoverTicks = syncQPCValue - (wholeSeconds * mQPCFrequency);
*ust = wholeSeconds * kMicrosecondsPerSecond +
leftoverTicks * kMicrosecondsPerSecond / mQPCFrequency;
}
return egl::Error(EGL_SUCCESS);
}
} // namespace rx } // namespace rx
...@@ -49,6 +49,8 @@ class SwapChain11 final : public SwapChainD3D ...@@ -49,6 +49,8 @@ class SwapChain11 final : public SwapChainD3D
EGLint getHeight() const { return mHeight; } EGLint getHeight() const { return mHeight; }
void *getKeyedMutex() override { return mKeyedMutex; } void *getKeyedMutex() override { return mKeyedMutex; }
egl::Error getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) override;
private: private:
void release(); void release();
void initPassThroughResources(); void initPassThroughResources();
...@@ -101,6 +103,8 @@ class SwapChain11 final : public SwapChainD3D ...@@ -101,6 +103,8 @@ class SwapChain11 final : public SwapChainD3D
SurfaceRenderTarget11 mColorRenderTarget; SurfaceRenderTarget11 mColorRenderTarget;
SurfaceRenderTarget11 mDepthStencilRenderTarget; SurfaceRenderTarget11 mDepthStencilRenderTarget;
LONGLONG mQPCFrequency;
}; };
} // namespace rx } // namespace rx
......
...@@ -397,6 +397,12 @@ void *SwapChain9::getKeyedMutex() ...@@ -397,6 +397,12 @@ void *SwapChain9::getKeyedMutex()
return nullptr; return nullptr;
} }
egl::Error SwapChain9::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
{
UNREACHABLE();
return egl::Error(EGL_BAD_SURFACE);
}
void SwapChain9::recreate() void SwapChain9::recreate()
{ {
if (!mSwapChain) if (!mSwapChain)
......
...@@ -46,6 +46,8 @@ class SwapChain9 : public SwapChainD3D ...@@ -46,6 +46,8 @@ class SwapChain9 : public SwapChainD3D
void *getKeyedMutex() override; void *getKeyedMutex() override;
egl::Error getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) override;
private: private:
void release(); void release();
......
...@@ -28,4 +28,10 @@ FramebufferImpl *SurfaceGL::createDefaultFramebuffer(const gl::FramebufferState ...@@ -28,4 +28,10 @@ FramebufferImpl *SurfaceGL::createDefaultFramebuffer(const gl::FramebufferState
return new FramebufferGL(data, mRenderer->getFunctions(), mRenderer->getStateManager(), return new FramebufferGL(data, mRenderer->getFunctions(), mRenderer->getStateManager(),
mRenderer->getWorkarounds(), mRenderer->getBlitter(), true); mRenderer->getWorkarounds(), mRenderer->getBlitter(), true);
} }
egl::Error SurfaceGL::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
{
UNREACHABLE();
return egl::Error(EGL_BAD_SURFACE);
}
} }
...@@ -23,6 +23,7 @@ class SurfaceGL : public SurfaceImpl ...@@ -23,6 +23,7 @@ class SurfaceGL : public SurfaceImpl
~SurfaceGL() override; ~SurfaceGL() override;
FramebufferImpl *createDefaultFramebuffer(const gl::FramebufferState &data) override; FramebufferImpl *createDefaultFramebuffer(const gl::FramebufferState &data) override;
egl::Error getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) override;
virtual egl::Error makeCurrent() = 0; virtual egl::Error makeCurrent() = 0;
......
...@@ -64,6 +64,12 @@ egl::Error SurfaceVk::releaseTexImage(EGLint buffer) ...@@ -64,6 +64,12 @@ egl::Error SurfaceVk::releaseTexImage(EGLint buffer)
return egl::Error(EGL_BAD_ACCESS); return egl::Error(EGL_BAD_ACCESS);
} }
egl::Error SurfaceVk::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
{
UNIMPLEMENTED();
return egl::Error(EGL_BAD_ACCESS);
}
void SurfaceVk::setSwapInterval(EGLint interval) void SurfaceVk::setSwapInterval(EGLint interval)
{ {
UNIMPLEMENTED(); UNIMPLEMENTED();
......
...@@ -28,6 +28,7 @@ class SurfaceVk : public SurfaceImpl ...@@ -28,6 +28,7 @@ class SurfaceVk : public SurfaceImpl
egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override; egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override;
egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override; egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override;
egl::Error releaseTexImage(EGLint buffer) override; egl::Error releaseTexImage(EGLint buffer) override;
egl::Error getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) override;
void setSwapInterval(EGLint interval) override; void setSwapInterval(EGLint interval) override;
// width and height can change with client window resizing // width and height can change with client window resizing
......
...@@ -1576,4 +1576,52 @@ Error ValidateStreamPostD3DTextureNV12ANGLE(const Display *display, ...@@ -1576,4 +1576,52 @@ Error ValidateStreamPostD3DTextureNV12ANGLE(const Display *display,
return stream->validateD3D11NV12Texture(texture); return stream->validateD3D11NV12Texture(texture);
} }
Error ValidateGetSyncValuesCHROMIUM(const Display *display,
const Surface *surface,
const EGLuint64KHR *ust,
const EGLuint64KHR *msc,
const EGLuint64KHR *sbc)
{
ANGLE_TRY(ValidateDisplay(display));
const DisplayExtensions &displayExtensions = display->getExtensions();
if (!displayExtensions.getSyncValues)
{
return Error(EGL_BAD_ACCESS, "getSyncValues extension not active");
}
if (display->isDeviceLost())
{
return Error(EGL_CONTEXT_LOST, "Context is lost.");
}
if (surface == EGL_NO_SURFACE)
{
return Error(EGL_BAD_SURFACE, "getSyncValues surface cannot be EGL_NO_SURFACE");
}
if (!surface->directComposition())
{
return Error(EGL_BAD_SURFACE,
"getSyncValues surface requires Direct Composition to be enabled");
}
if (ust == nullptr)
{
return egl::Error(EGL_BAD_PARAMETER, "ust is null");
}
if (msc == nullptr)
{
return egl::Error(EGL_BAD_PARAMETER, "msc is null");
}
if (sbc == nullptr)
{
return egl::Error(EGL_BAD_PARAMETER, "sbc is null");
}
return Error(EGL_SUCCESS);
}
} // namespace gl } // namespace gl
...@@ -94,6 +94,11 @@ Error ValidateStreamPostD3DTextureNV12ANGLE(const Display *display, ...@@ -94,6 +94,11 @@ Error ValidateStreamPostD3DTextureNV12ANGLE(const Display *display,
const Stream *stream, const Stream *stream,
void *texture, void *texture,
const AttributeMap &attribs); const AttributeMap &attribs);
Error ValidateGetSyncValuesCHROMIUM(const Display *display,
const Surface *surface,
const EGLuint64KHR *ust,
const EGLuint64KHR *msc,
const EGLuint64KHR *sbc);
// Other validation // Other validation
Error ValidateCompatibleConfigs(const Display *display, Error ValidateCompatibleConfigs(const Display *display,
......
...@@ -358,4 +358,13 @@ EGLBoolean EGLAPIENTRY eglStreamPostD3DTextureNV12ANGLE(EGLDisplay dpy, ...@@ -358,4 +358,13 @@ EGLBoolean EGLAPIENTRY eglStreamPostD3DTextureNV12ANGLE(EGLDisplay dpy,
{ {
return egl::StreamPostD3DTextureNV12ANGLE(dpy, stream, texture, attrib_list); return egl::StreamPostD3DTextureNV12ANGLE(dpy, stream, texture, attrib_list);
} }
EGLBoolean EGLAPIENTRY eglGetSyncValuesCHROMIUM(EGLDisplay dpy,
EGLSurface surface,
EGLuint64KHR *ust,
EGLuint64KHR *msc,
EGLuint64KHR *sbc)
{
return egl::GetSyncValuesCHROMIUM(dpy, surface, ust, msc, sbc);
}
} }
...@@ -57,6 +57,7 @@ EXPORTS ...@@ -57,6 +57,7 @@ EXPORTS
eglStreamConsumerGLTextureExternalAttribsNV @63 eglStreamConsumerGLTextureExternalAttribsNV @63
eglCreateStreamProducerD3DTextureNV12ANGLE @64 eglCreateStreamProducerD3DTextureNV12ANGLE @64
eglStreamPostD3DTextureNV12ANGLE @65 eglStreamPostD3DTextureNV12ANGLE @65
eglGetSyncValuesCHROMIUM @66
; 1.5 entry points ; 1.5 entry points
eglCreateSync @38 eglCreateSync @38
......
...@@ -1808,6 +1808,8 @@ __eglMustCastToProperFunctionPointerType EGLAPIENTRY GetProcAddress(const char * ...@@ -1808,6 +1808,8 @@ __eglMustCastToProperFunctionPointerType EGLAPIENTRY GetProcAddress(const char *
INSERT_PROC_ADDRESS(egl, CreateStreamProducerD3DTextureNV12ANGLE); INSERT_PROC_ADDRESS(egl, CreateStreamProducerD3DTextureNV12ANGLE);
INSERT_PROC_ADDRESS(egl, StreamPostD3DTextureNV12ANGLE); INSERT_PROC_ADDRESS(egl, StreamPostD3DTextureNV12ANGLE);
// EGL_CHROMIUM_get_sync_values
INSERT_PROC_ADDRESS(egl, GetSyncValuesCHROMIUM);
#undef INSERT_PROC_ADDRESS #undef INSERT_PROC_ADDRESS
return map; return map;
}; };
......
...@@ -895,4 +895,36 @@ EGLBoolean EGLAPIENTRY StreamPostD3DTextureNV12ANGLE(EGLDisplay dpy, ...@@ -895,4 +895,36 @@ EGLBoolean EGLAPIENTRY StreamPostD3DTextureNV12ANGLE(EGLDisplay dpy,
SetGlobalError(error); SetGlobalError(error);
return EGL_TRUE; return EGL_TRUE;
} }
EGLBoolean EGLAPIENTRY GetSyncValuesCHROMIUM(EGLDisplay dpy,
EGLSurface surface,
EGLuint64KHR *ust,
EGLuint64KHR *msc,
EGLuint64KHR *sbc)
{
EVENT(
"(EGLDisplay dpy = 0x%0.8p, EGLSurface surface = 0x%0.8p, EGLuint64KHR* ust = 0x%0.8p, "
"EGLuint64KHR* msc = 0x%0.8p, EGLuint64KHR* sbc = 0x%0.8p",
dpy, surface, ust, msc, sbc);
Display *display = static_cast<Display *>(dpy);
Surface *eglSurface = static_cast<Surface *>(surface);
Error error = ValidateGetSyncValuesCHROMIUM(display, eglSurface, ust, msc, sbc);
if (error.isError())
{
SetGlobalError(error);
return EGL_FALSE;
}
error = eglSurface->getSyncValues(ust, msc, sbc);
if (error.isError())
{
SetGlobalError(error);
return EGL_FALSE;
}
SetGlobalError(error);
return EGL_TRUE;
}
} }
...@@ -79,6 +79,14 @@ ANGLE_EXPORT EGLBoolean EGLAPIENTRY StreamPostD3DTextureNV12ANGLE(EGLDisplay dpy ...@@ -79,6 +79,14 @@ ANGLE_EXPORT EGLBoolean EGLAPIENTRY StreamPostD3DTextureNV12ANGLE(EGLDisplay dpy
EGLStreamKHR stream, EGLStreamKHR stream,
void *texture, void *texture,
const EGLAttrib *attrib_list); const EGLAttrib *attrib_list);
// EGL_CHROMIUM_get_sync_values
ANGLE_EXPORT EGLBoolean EGLAPIENTRY GetSyncValuesCHROMIUM(EGLDisplay dpy,
EGLSurface surface,
EGLuint64KHR *ust,
EGLuint64KHR *msc,
EGLuint64KHR *sbc);
} // namespace egl } // namespace egl
#endif // LIBGLESV2_ENTRYPOINTSEGLEXT_H_ #endif // LIBGLESV2_ENTRYPOINTSEGLEXT_H_
...@@ -101,6 +101,7 @@ ...@@ -101,6 +101,7 @@
'<(angle_path)/src/tests/egl_tests/EGLDeviceTest.cpp', '<(angle_path)/src/tests/egl_tests/EGLDeviceTest.cpp',
'<(angle_path)/src/tests/egl_tests/EGLPresentPathD3D11Test.cpp', '<(angle_path)/src/tests/egl_tests/EGLPresentPathD3D11Test.cpp',
'<(angle_path)/src/tests/egl_tests/EGLStreamTest.cpp', '<(angle_path)/src/tests/egl_tests/EGLStreamTest.cpp',
'<(angle_path)/src/tests/egl_tests/EGLSyncControlTest.cpp',
# TODO(cwallez) for Linux, requires a portable implementation of threads # TODO(cwallez) for Linux, requires a portable implementation of threads
'<(angle_path)/src/tests/egl_tests/EGLThreadTest.cpp', '<(angle_path)/src/tests/egl_tests/EGLThreadTest.cpp',
'<(angle_path)/src/tests/egl_tests/media/yuvtest.inl', '<(angle_path)/src/tests/egl_tests/media/yuvtest.inl',
......
//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#ifndef ANGLE_ENABLE_D3D9
#define ANGLE_ENABLE_D3D9
#endif
#ifndef ANGLE_ENABLE_D3D11
#define ANGLE_ENABLE_D3D11
#endif
#include <d3d11.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "com_utils.h"
#include "OSWindow.h"
#include "test_utils/ANGLETest.h"
using namespace angle;
typedef EGLBoolean(EGLAPIENTRYP PFNEGLGETSYNCVALUESCHROMIUMPROC)(EGLDisplay dpy,
EGLSurface surface,
EGLuint64KHR *ust,
EGLuint64KHR *msc,
EGLuint64KHR *sbc);
class EGLSyncControlTest : public testing::Test
{
protected:
EGLSyncControlTest() {}
void SetUp() override
{
mD3D11Module = LoadLibrary(TEXT("d3d11.dll"));
if (mD3D11Module == nullptr)
{
std::cout << "Unable to LoadLibrary D3D11" << std::endl;
return;
}
mD3D11CreateDevice = reinterpret_cast<PFN_D3D11_CREATE_DEVICE>(
GetProcAddress(mD3D11Module, "D3D11CreateDevice"));
if (mD3D11CreateDevice == nullptr)
{
std::cout << "Could not retrieve D3D11CreateDevice from d3d11.dll" << std::endl;
return;
}
mD3D11Available = true;
const char *extensionString =
static_cast<const char *>(eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS));
if (strstr(extensionString, "EGL_ANGLE_device_creation"))
{
if (strstr(extensionString, "EGL_ANGLE_device_creation_d3d11"))
{
mDeviceCreationD3D11ExtAvailable = true;
}
}
eglGetSyncValuesCHROMIUM = reinterpret_cast<PFNEGLGETSYNCVALUESCHROMIUMPROC>(
eglGetProcAddress("eglGetSyncValuesCHROMIUM"));
}
void TearDown() override
{
SafeRelease(mDevice);
SafeRelease(mDeviceContext);
SafeDelete(mOSWindow);
if (mSurface != EGL_NO_SURFACE)
{
eglDestroySurface(mDisplay, mSurface);
mSurface = EGL_NO_SURFACE;
}
if (mContext != EGL_NO_CONTEXT)
{
eglDestroyContext(mDisplay, mContext);
mContext = EGL_NO_CONTEXT;
}
if (mDisplay != EGL_NO_DISPLAY)
{
eglTerminate(mDisplay);
mDisplay = EGL_NO_DISPLAY;
}
}
void CreateD3D11Device()
{
ASSERT_TRUE(mD3D11Available);
ASSERT_EQ(nullptr, mDevice); // The device shouldn't be created twice
HRESULT hr =
mD3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, 0, nullptr, 0,
D3D11_SDK_VERSION, &mDevice, &mFeatureLevel, &mDeviceContext);
ASSERT_TRUE(SUCCEEDED(hr));
}
void InitializeDisplay()
{
EGLint displayAttribs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE,
EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE,
EGL_DONT_CARE,
EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE,
EGL_DONT_CARE,
EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
EGL_NONE};
// Create an OS Window
mOSWindow = CreateOSWindow();
mOSWindow->initialize("EGLSyncControlTest", 64, 64);
// Create an EGLDisplay using the EGLDevice
mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
reinterpret_cast<void *>(mOSWindow->getNativeDisplay()),
displayAttribs);
ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY);
EGLint majorVersion, minorVersion;
ASSERT_TRUE(eglInitialize(mDisplay, &majorVersion, &minorVersion) == EGL_TRUE);
}
void CreateWindowSurface()
{
eglBindAPI(EGL_OPENGL_ES_API);
ASSERT_EGL_SUCCESS();
// Choose a config
const EGLint configAttributes[] = {EGL_NONE};
EGLint configCount = 0;
ASSERT_EGL_TRUE(eglChooseConfig(mDisplay, configAttributes, &mConfig, 1, &configCount));
const EGLint surfaceAttributes[] = {EGL_DIRECT_COMPOSITION_ANGLE, EGL_TRUE, EGL_NONE};
// Create window surface
mSurface = eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(),
surfaceAttributes);
if (mSurface == nullptr)
{
std::cout << "Unable to create window surface with Direct Composition" << std::endl;
return;
}
mDirectCompositionSurfaceAvailable = true;
// Create EGL context
EGLint contextAttibutes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
mContext = eglCreateContext(mDisplay, mConfig, nullptr, contextAttibutes);
ASSERT_EGL_SUCCESS();
// Make the surface current
eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);
ASSERT_EGL_SUCCESS();
}
bool mD3D11Available = false;
HMODULE mD3D11Module = nullptr;
PFN_D3D11_CREATE_DEVICE mD3D11CreateDevice = nullptr;
ID3D11Device *mDevice = nullptr;
ID3D11DeviceContext *mDeviceContext = nullptr;
D3D_FEATURE_LEVEL mFeatureLevel;
bool mDeviceCreationD3D11ExtAvailable = false;
bool mDirectCompositionSurfaceAvailable = false;
OSWindow *mOSWindow = nullptr;
EGLDisplay mDisplay = EGL_NO_DISPLAY;
EGLSurface mSurface = EGL_NO_SURFACE;
EGLContext mContext = EGL_NO_CONTEXT;
EGLConfig mConfig = 0;
PFNEGLGETSYNCVALUESCHROMIUMPROC eglGetSyncValuesCHROMIUM = nullptr;
};
// Basic test for eglGetSyncValuesCHROMIUM extension. Verifies that eglGetSyncValuesCHROMIUM
// can be called on DX11 with direct composition and that it returns reasonable enough values.
TEST_F(EGLSyncControlTest, SyncValuesTest)
{
if (!mD3D11Available)
{
std::cout << "D3D11 not available, skipping test" << std::endl;
return;
}
CreateD3D11Device();
InitializeDisplay();
CreateWindowSurface();
if (!mDirectCompositionSurfaceAvailable)
{
std::cout << "Direct Composition surface not available, skipping test" << std::endl;
return;
}
const char *extensionString =
static_cast<const char *>(eglQueryString(mDisplay, EGL_EXTENSIONS));
ASSERT_TRUE(strstr(extensionString, "EGL_CHROMIUM_sync_control"));
EGLuint64KHR ust = 0, msc = 0, sbc = 0;
// It appears there is a race condition so the very first call to eglGetSyncValuesCHROMIUM
// can fail within D3D with DXGI_ERROR_FRAME_STATISTICS_DISJOINT.
// Should that be handled inside eglGetSyncValuesCHROMIUM?
eglGetSyncValuesCHROMIUM(mDisplay, mSurface, &ust, &msc, &sbc);
ASSERT_EGL_TRUE(eglGetSyncValuesCHROMIUM(mDisplay, mSurface, &ust, &msc, &sbc));
// Initial msc and sbc value should be true. Initial ust value is unspecified.
ASSERT_EQ(0ull, msc);
ASSERT_EQ(0ull, sbc);
// Perform some very basic rendering.
glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
ASSERT_EGL_TRUE(eglSwapBuffers(mDisplay, mSurface));
// Poll until sbc value increases. Normally it should change within 16-17 ms.
for (int i = 0; i < 100; i++)
{
::Sleep(1);
ASSERT_EGL_TRUE(eglGetSyncValuesCHROMIUM(mDisplay, mSurface, &ust, &msc, &sbc));
if (sbc > 0)
break;
}
// sbc should change to 1. msc and ust to some non-zero values.
ASSERT_EQ(1ull, sbc);
ASSERT_GT(ust, 0ull);
ASSERT_GT(msc, 0ull);
// Perform more rendering.
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
ASSERT_EGL_TRUE(eglSwapBuffers(mDisplay, mSurface));
// Poll until sbc value increases. Normally it should change within 16-17 ms.
EGLuint64KHR ust2 = 0, msc2 = 0, sbc2 = 0;
for (int i = 0; i < 100; i++)
{
::Sleep(1);
ASSERT_EGL_TRUE(eglGetSyncValuesCHROMIUM(mDisplay, mSurface, &ust2, &msc2, &sbc2));
if (sbc2 > sbc)
break;
}
// sbc2 should be 2. msc2 and ust2 should be greater than previous msc and ust values.
ASSERT_EQ(2ull, sbc2);
ASSERT_GT((ust2 - ust), 0ull);
ASSERT_GT((msc2 - msc), 0ull);
}
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