diff --git a/src/3rdparty/angle/src/common/platform.h b/src/3rdparty/angle/src/common/platform.h index fb251da57..2e1799455 100644 --- a/src/3rdparty/angle/src/common/platform.h +++ b/src/3rdparty/angle/src/common/platform.h @@ -59,12 +59,14 @@ # endif # if defined(ANGLE_ENABLE_D3D11) -#include -#include -#include -#include -#include -#include +# include +# include +# include +# include +# include +# include +# include // WARNING: This is actually D3D12! +# include # endif #if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11) diff --git a/src/3rdparty/angle/src/libANGLE/Caps.cpp b/src/3rdparty/angle/src/libANGLE/Caps.cpp index 44da2bbe2..208845745 100644 --- a/src/3rdparty/angle/src/libANGLE/Caps.cpp +++ b/src/3rdparty/angle/src/libANGLE/Caps.cpp @@ -1101,7 +1101,10 @@ DisplayExtensions::DisplayExtensions() displayTextureShareGroup(false), createContextClientArrays(false), programCacheControl(false), - robustResourceInitialization(false) + robustResourceInitialization(false), + colorspaceSRGB(false), + colorspaceSCRGBLinear(false), + colorspaceBt2020PQ(false) { } @@ -1146,6 +1149,9 @@ std::vector DisplayExtensions::getStrings() const InsertExtensionString("EGL_ANGLE_create_context_client_arrays", createContextClientArrays, &extensionStrings); InsertExtensionString("EGL_ANGLE_program_cache_control", programCacheControl, &extensionStrings); InsertExtensionString("EGL_ANGLE_robust_resource_initialization", robustResourceInitialization, &extensionStrings); + InsertExtensionString("EGL_KHR_gl_colorspace", colorspaceSRGB, &extensionStrings); + InsertExtensionString("EGL_EXT_gl_colorspace_scrgb_linear", colorspaceSCRGBLinear, &extensionStrings); + InsertExtensionString("EGL_EXT_gl_colorspace_bt2020_pq", colorspaceBt2020PQ, &extensionStrings); // TODO(jmadill): Enable this when complete. //InsertExtensionString("KHR_create_context_no_error", createContextNoError, &extensionStrings); // clang-format on diff --git a/src/3rdparty/angle/src/libANGLE/Caps.h b/src/3rdparty/angle/src/libANGLE/Caps.h index 64bdf9711..8157af580 100644 --- a/src/3rdparty/angle/src/libANGLE/Caps.h +++ b/src/3rdparty/angle/src/libANGLE/Caps.h @@ -692,6 +692,15 @@ struct DisplayExtensions // EGL_ANGLE_robust_resource_initialization bool robustResourceInitialization; + + // EGL_KHR_gl_colorspace + bool colorspaceSRGB; + + // EGL_EXT_gl_colorspace_scrgb_linear + bool colorspaceSCRGBLinear; + + // EGL_EXT_gl_colorspace_bt2020_pq + bool colorspaceBt2020PQ; }; struct DeviceExtensions diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/RendererD3D.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/RendererD3D.h index dcc98f2ec..b8ee63562 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/RendererD3D.h +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/RendererD3D.h @@ -130,7 +130,8 @@ class RendererD3D : public BufferFactoryD3D, public MultisampleTextureInitialize GLenum backBufferFormat, GLenum depthBufferFormat, EGLint orientation, - EGLint samples) = 0; + EGLint samples, + EGLint colorSpace) = 0; virtual egl::Error getD3DTextureInfo(const egl::Config *configuration, IUnknown *d3dTexture, EGLint *width, diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp index 7657aef79..efd4dd1a2 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp @@ -22,6 +22,27 @@ namespace rx { +GLenum renderTargetFormatFromColorSpace(egl::Display *display, GLenum baseFormat, EGLint colorSpace) +{ + GLenum result = baseFormat; + + /** + * If sRGB extension is supported, we should change the surface format + * to a specific one that does support automated gamma conversion. + * + * TODO: openGL doesn't support BGRA-sRGB texture format, so creation of + * textures in this format technically is not supported! + */ + if (display->getExtensions().colorspaceSRGB && + baseFormat == GL_RGBA8_OES && + colorSpace == EGL_GL_COLORSPACE_SRGB_KHR) + { + result = GL_SRGB8_ALPHA8; + } + + return result; +} + SurfaceD3D::SurfaceD3D(const egl::SurfaceState &state, RendererD3D *renderer, egl::Display *display, @@ -34,7 +55,8 @@ SurfaceD3D::SurfaceD3D(const egl::SurfaceState &state, mDisplay(display), mFixedSize(window == nullptr || attribs.get(EGL_FIXED_SIZE_ANGLE, EGL_FALSE) == EGL_TRUE), mOrientation(static_cast(attribs.get(EGL_SURFACE_ORIENTATION_ANGLE, 0))), - mRenderTargetFormat(state.config->renderTargetFormat), + mColorSpace(static_cast(attribs.get(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_LINEAR_KHR))), + mRenderTargetFormat(renderTargetFormatFromColorSpace(display, state.config->renderTargetFormat, mColorSpace)), mDepthStencilFormat(state.config->depthStencilFormat), mSwapChain(nullptr), mSwapIntervalDirty(true), @@ -148,7 +170,7 @@ egl::Error SurfaceD3D::resetSwapChain(const egl::Display *display) mSwapChain = mRenderer->createSwapChain(mNativeWindow, mShareHandle, mD3DTexture, mRenderTargetFormat, - mDepthStencilFormat, mOrientation, mState.config->samples); + mDepthStencilFormat, mOrientation, mState.config->samples, mColorSpace); if (!mSwapChain) { return egl::EglBadAlloc(); diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h index 01d257324..ccb793d42 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h @@ -90,6 +90,7 @@ class SurfaceD3D : public SurfaceImpl bool mFixedSize; GLint mOrientation; + EGLint mColorSpace; GLenum mRenderTargetFormat; GLenum mDepthStencilFormat; diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp index b0ef9abdd..f0e497b52 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp @@ -465,6 +465,7 @@ Renderer11::Renderer11(egl::Display *display) mRenderer11DeviceCaps.supportsConstantBufferOffsets = false; mRenderer11DeviceCaps.supportsVpRtIndexWriteFromVertexShader = false; mRenderer11DeviceCaps.supportsDXGI1_2 = false; + mRenderer11DeviceCaps.supportsDXGI1_4 = false; mRenderer11DeviceCaps.B5G6R5support = 0; mRenderer11DeviceCaps.B4G4R4A4support = 0; mRenderer11DeviceCaps.B5G5R5A1support = 0; @@ -918,6 +919,7 @@ egl::Error Renderer11::initializeDevice() // Gather stats on DXGI and D3D feature level ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.SupportsDXGI1_2", mRenderer11DeviceCaps.supportsDXGI1_2); + ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.SupportsDXGI1_4", mRenderer11DeviceCaps.supportsDXGI1_4); ANGLEFeatureLevel angleFeatureLevel = GetANGLEFeatureLevel(mRenderer11DeviceCaps.featureLevel); @@ -1002,6 +1004,10 @@ void Renderer11::populateRenderer11DeviceCaps() IDXGIAdapter2 *dxgiAdapter2 = d3d11::DynamicCastComObject(mDxgiAdapter); mRenderer11DeviceCaps.supportsDXGI1_2 = (dxgiAdapter2 != nullptr); SafeRelease(dxgiAdapter2); + + IDXGIAdapter3 *dxgiAdapter3 = d3d11::DynamicCastComObject(mDxgiAdapter); + mRenderer11DeviceCaps.supportsDXGI1_4 = (dxgiAdapter3 != nullptr); + SafeRelease(dxgiAdapter3); } gl::SupportedSampleSet Renderer11::generateSampleSetForEGLConfig( @@ -1241,6 +1247,11 @@ void Renderer11::generateDisplayExtensions(egl::DisplayExtensions *outExtensions // All D3D feature levels support robust resource init outExtensions->robustResourceInitialization = true; + + // color space selection supported in DXGI 1.4 only + outExtensions->colorspaceSRGB = mRenderer11DeviceCaps.supportsDXGI1_4; + outExtensions->colorspaceSCRGBLinear = mRenderer11DeviceCaps.supportsDXGI1_4; + outExtensions->colorspaceBt2020PQ = mRenderer11DeviceCaps.supportsDXGI1_4; } gl::Error Renderer11::flush() @@ -1436,10 +1447,11 @@ SwapChainD3D *Renderer11::createSwapChain(NativeWindowD3D *nativeWindow, GLenum backBufferFormat, GLenum depthBufferFormat, EGLint orientation, - EGLint samples) + EGLint samples, + EGLint colorSpace) { return new SwapChain11(this, GetAs(nativeWindow), shareHandle, d3dTexture, - backBufferFormat, depthBufferFormat, orientation, samples); + backBufferFormat, depthBufferFormat, orientation, samples, colorSpace); } void *Renderer11::getD3DDevice() diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h index a8c24e681..3516bf779 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h @@ -49,6 +49,7 @@ struct Renderer11DeviceCaps D3D_FEATURE_LEVEL featureLevel; bool supportsDXGI1_2; // Support for DXGI 1.2 + bool supportsDXGI1_4; // Support for DXGI 1.4 bool supportsClearView; // Support for ID3D11DeviceContext1::ClearView bool supportsConstantBufferOffsets; // Support for Constant buffer offset bool supportsVpRtIndexWriteFromVertexShader; // VP/RT can be selected in the Vertex Shader @@ -138,7 +139,8 @@ class Renderer11 : public RendererD3D GLenum backBufferFormat, GLenum depthBufferFormat, EGLint orientation, - EGLint samples) override; + EGLint samples, + EGLint colorSpace) override; egl::Error getD3DTextureInfo(const egl::Config *configuration, IUnknown *d3dTexture, EGLint *width, diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp index dcfd06484..fc967b90d 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp @@ -18,6 +18,11 @@ #include "libANGLE/renderer/d3d/d3d11/texture_format_table.h" #include "third_party/trace_event/trace_event.h" +#if 0 +// used only for HDR metadata configuration options +#include +#endif + // Precompiled shaders #include "libANGLE/renderer/d3d/d3d11/shaders/compiled/passthrough2d11vs.h" #include "libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2d11ps.h" @@ -56,12 +61,14 @@ SwapChain11::SwapChain11(Renderer11 *renderer, GLenum backBufferFormat, GLenum depthBufferFormat, EGLint orientation, - EGLint samples) + EGLint samples, + EGLint colorSpace) : SwapChainD3D(shareHandle, d3dTexture, backBufferFormat, depthBufferFormat), mRenderer(renderer), mWidth(-1), mHeight(-1), mOrientation(orientation), + mColorSpace(colorSpace), mAppCreatedShareHandle(mShareHandle != nullptr), mSwapInterval(0), mPassThroughResourcesInit(false), @@ -620,10 +627,92 @@ EGLint SwapChain11::reset(const gl::Context *context, mSwapChain1 = d3d11::DynamicCastComObject(mSwapChain); } + if (mRenderer->getRenderer11DeviceCaps().supportsDXGI1_4) + { + IDXGISwapChain3 *swapChain3 = d3d11::DynamicCastComObject(mSwapChain); + + printf("*** EGL colorSpace: 0x%X\n", mColorSpace); + printf("*** EGL format: 0x%X\n", mOffscreenRenderTargetFormat); + printf("*** Native format: 0x%X\n", getSwapChainNativeFormat()); + + if (mColorSpace != EGL_GL_COLORSPACE_LINEAR_KHR) { + DXGI_COLOR_SPACE_TYPE nativeColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + switch (mColorSpace) + { + case EGL_GL_COLORSPACE_SRGB_KHR: + nativeColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + break; + case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT: + nativeColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; + break; + case EGL_GL_COLORSPACE_BT2020_PQ_EXT: + nativeColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + break; + default: + ASSERT(0 && "Unsupported colorspace requested"); + } + + printf("*** Native colorSpace: 0x%X\n", nativeColorSpace); + + UINT supported = 0; + result = swapChain3->CheckColorSpaceSupport(nativeColorSpace, &supported); + ASSERT(SUCCEEDED(result)); + if (!(supported & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) { + SafeRelease(swapChain3); + return EGL_BAD_MATCH; + } + + result = swapChain3->SetColorSpace1(nativeColorSpace); + ASSERT(SUCCEEDED(result)); + } + + SafeRelease(swapChain3); + +#if 0 + + IDXGISwapChain4 *swapChain4 = d3d11::DynamicCastComObject(mSwapChain); + + DXGI_HDR_METADATA_HDR10 md; + md.RedPrimary[0] = 0.680 * 50000; + md.RedPrimary[1] = 0.320 * 50000; + md.GreenPrimary[0] = 0.265 * 50000; + md.GreenPrimary[1] = 0.690 * 50000; + md.BluePrimary[0] = 0.150 * 50000; + md.BluePrimary[1] = 0.060 * 50000; + md.WhitePoint[0] = 0.3127 * 50000; + md.WhitePoint[1] = 0.3290 * 50000; + md.MaxMasteringLuminance = 1000 * 10000; + md.MinMasteringLuminance = 0.001 * 10000; + md.MaxContentLightLevel = 1000; + md.MaxFrameAverageLightLevel = 200; + result = swapChain4->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10, sizeof(md), &md); + printf("*** Result hdr 0x%X\n", result); + SafeRelease(swapChain4); +#endif + } + ID3D11Texture2D *backbufferTex = nullptr; result = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast(&backbufferTex)); ASSERT(SUCCEEDED(result)); + + // TODO: recover rendering to sRGB + // + // D3D11_RENDER_TARGET_VIEW_DESC offscreenRTVDesc; + // offscreenRTVDesc.Format = getSwapChainNativeFormat(); + // + // if (mColorSpace == EGL_GL_COLORSPACE_SRGB_KHR) { + // if (offscreenRTVDesc.Format == DXGI_FORMAT_R8G8B8A8_UNORM) { + // offscreenRTVDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + // } + // + // if (offscreenRTVDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM) { + // offscreenRTVDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + // } + // } + // + // printf("*** Render target format: 0x%X\n", offscreenRTVDesc.Format); + const auto &format = d3d11::Format::Get(mOffscreenRenderTargetFormat, mRenderer->getRenderer11DeviceCaps()); mBackBufferTexture.set(backbufferTex, format); diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h index eca068210..2a4b9ba27 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h @@ -28,7 +28,8 @@ class SwapChain11 final : public SwapChainD3D GLenum backBufferFormat, GLenum depthBufferFormat, EGLint orientation, - EGLint samples); + EGLint samples, + EGLint colorSpace); ~SwapChain11() override; EGLint resize(const gl::Context *context, @@ -93,6 +94,7 @@ class SwapChain11 final : public SwapChainD3D EGLint mWidth; EGLint mHeight; const EGLint mOrientation; + EGLint mColorSpace; bool mAppCreatedShareHandle; unsigned int mSwapInterval; bool mPassThroughResourcesInit; diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp index 5394e3d3e..af52c41d0 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp @@ -146,6 +146,9 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, // Use IDXGIFactory2::CreateSwapChainForHwnd if DXGI 1.2 is available to create a // DXGI_SWAP_EFFECT_SEQUENTIAL swap chain. + // + // NOTE: in non-flip mode HDR rendering is not supported, so use it + // by default IDXGIFactory2 *factory2 = d3d11::DynamicCastComObject(factory); if (factory2 != nullptr) { @@ -158,9 +161,9 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_BACK_BUFFER; - swapChainDesc.BufferCount = 1; + swapChainDesc.BufferCount = 2; swapChainDesc.Scaling = DXGI_SCALING_STRETCH; - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; swapChainDesc.Flags = 0; IDXGISwapChain1 *swapChain1 = nullptr; @@ -176,7 +179,7 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, } DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; - swapChainDesc.BufferCount = 1; + swapChainDesc.BufferCount = 2; swapChainDesc.BufferDesc.Format = format; swapChainDesc.BufferDesc.Width = width; swapChainDesc.BufferDesc.Height = height; @@ -191,6 +194,16 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, swapChainDesc.SampleDesc.Count = samples; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.Windowed = TRUE; + + /** + * NOTE1: in discard mode the swap chain doesn't support partial + * presentatiopn with Present1() call. Though it is not a big + * problem, because in case DXGI 1.2 is supported this code is + * unreachable. + * + * NOTE2: Flip modes are not supported on Windows 7 and the like, + * so use a legacy mode as a fallback + */ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; HRESULT result = factory->CreateSwapChain(device, &swapChainDesc, swapChain); diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp index 75c629886..58596169a 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp @@ -718,8 +718,10 @@ SwapChainD3D *Renderer9::createSwapChain(NativeWindowD3D *nativeWindow, GLenum backBufferFormat, GLenum depthBufferFormat, EGLint orientation, - EGLint samples) + EGLint samples, + EGLint colorSpace) { + UNUSED_VARIABLE(colorSpace); return new SwapChain9(this, GetAs(nativeWindow), shareHandle, d3dTexture, backBufferFormat, depthBufferFormat, orientation); } diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h index 9ddee45f0..ce4bb201e 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h @@ -92,7 +92,8 @@ class Renderer9 : public RendererD3D GLenum backBufferFormat, GLenum depthBufferFormat, EGLint orientation, - EGLint samples) override; + EGLint samples, + EGLint colorSpace) override; egl::Error getD3DTextureInfo(const egl::Config *configuration, IUnknown *d3dTexture, EGLint *width, diff --git a/src/3rdparty/angle/src/libANGLE/validationEGL.cpp b/src/3rdparty/angle/src/libANGLE/validationEGL.cpp index 13a3a9e28..858d7ee92 100644 --- a/src/3rdparty/angle/src/libANGLE/validationEGL.cpp +++ b/src/3rdparty/angle/src/libANGLE/validationEGL.cpp @@ -885,6 +885,32 @@ Error ValidateCreateWindowSurface(Display *display, Config *config, EGLNativeWin "either EGL_TRUE or EGL_FALSE."; } break; + case EGL_GL_COLORSPACE: + + if (!displayExtensions.colorspaceSRGB) + { + return EglBadAttribute() << "EGL_KHR_gl_colorspace is not supported on this platform."; + } + + if (value == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) + { + if (!displayExtensions.colorspaceSCRGBLinear) + { + return EglBadAttribute() << "EGL_EXT_gl_colorspace_scrgb_linear is not supported on this platform."; + } + } + else if (value == EGL_GL_COLORSPACE_BT2020_PQ_EXT) + { + if (!displayExtensions.colorspaceBt2020PQ) + { + return EglBadAttribute() << "EGL_EXT_gl_colorspace_bt2020_pq is not supported on this platform."; + } + } + else if (value != EGL_GL_COLORSPACE_SRGB_KHR && value != EGL_GL_COLORSPACE_LINEAR_KHR) + { + return EglBadAttribute() << "Unknown EGL color space requested"; + } + break; default: return EglBadAttribute(); @@ -977,6 +1003,33 @@ Error ValidateCreatePbufferSurface(Display *display, Config *config, const Attri } break; + case EGL_GL_COLORSPACE: + + if (!displayExtensions.colorspaceSRGB) + { + return EglBadAttribute() << "EGL_KHR_gl_colorspace is not supported on this platform."; + } + + if (value == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) + { + if (!displayExtensions.colorspaceSCRGBLinear) + { + return EglBadAttribute() << "EGL_EXT_gl_colorspace_scrgb_linear is not supported on this platform."; + } + } + else if (value == EGL_GL_COLORSPACE_BT2020_PQ_EXT) + { + if (!displayExtensions.colorspaceBt2020PQ) + { + return EglBadAttribute() << "EGL_EXT_gl_colorspace_bt2020_pq is not supported on this platform."; + } + } + else if (value != EGL_GL_COLORSPACE_SRGB_KHR && value != EGL_GL_COLORSPACE_LINEAR_KHR) + { + return EglBadAttribute() << "Unknown EGL color space requested"; + } + break; + default: return EglBadAttribute(); } diff --git a/src/gui/kernel/qsurfaceformat.cpp b/src/gui/kernel/qsurfaceformat.cpp index 1a814ec21..fc8b9c4f4 100644 --- a/src/gui/kernel/qsurfaceformat.cpp +++ b/src/gui/kernel/qsurfaceformat.cpp @@ -221,6 +221,17 @@ public: set, the window will be created with an sRGB-capable default framebuffer. Note that some platforms may return windows with a sRGB-capable default framebuffer even when not requested explicitly. + + \value scRGBColorSpace When \c{EGL_EXT_gl_colorspace_scrgb_linear} + is supported by the platform and this value is set, the window will + be created with an scRGB-capable default framebuffer. Note that some + platforms may return windows with a scRGB-capable default framebuffer + even when not requested explicitly. It usually happens when the application + requests 16-bit surface format. + + \value bt2020PQColorSpace When \c{EGL_EXT_gl_colorspace_bt2020_pq} + is supported by the platform and this value is set, the window will + be created with an bt2020 PQ default framebuffer. */ /*! diff --git a/src/gui/kernel/qsurfaceformat.h b/src/gui/kernel/qsurfaceformat.h index ed63eb8bb..9ba6a29b7 100644 --- a/src/gui/kernel/qsurfaceformat.h +++ b/src/gui/kernel/qsurfaceformat.h @@ -87,7 +87,9 @@ public: enum ColorSpace { DefaultColorSpace, - sRGBColorSpace + sRGBColorSpace, + scRGBColorSpace, + bt2020PQColorSpace }; Q_ENUM(ColorSpace) diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index cae3d516c..ccdccb637 100644 --- a/src/gui/opengl/qopenglframebufferobject.cpp +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -545,10 +545,13 @@ void QOpenGLFramebufferObjectPrivate::initTexture(int idx) ColorAttachment &color(colorAttachments[idx]); GLuint pixelType = GL_UNSIGNED_BYTE; - if (color.internalFormat == GL_RGB10_A2 || color.internalFormat == GL_RGB10) + if (color.internalFormat == GL_RGB10_A2 || color.internalFormat == GL_RGB10) { pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; - else if (color.internalFormat == GL_RGB16 || color.internalFormat == GL_RGBA16) + } else if (color.internalFormat == GL_RGB16 || color.internalFormat == GL_RGBA16) { pixelType = GL_UNSIGNED_SHORT; + } else if (color.internalFormat == GL_RGBA16F) { + pixelType = GL_HALF_FLOAT; + } funcs.glTexImage2D(target, 0, color.internalFormat, color.size.width(), color.size.height(), 0, GL_RGBA, pixelType, NULL); diff --git a/src/gui/opengl/qopengltextureblitter.cpp b/src/gui/opengl/qopengltextureblitter.cpp index b65df9dc8..5f6dbff29 100644 --- a/src/gui/opengl/qopengltextureblitter.cpp +++ b/src/gui/opengl/qopengltextureblitter.cpp @@ -131,14 +131,85 @@ static const char vertex_shader[] = "}"; static const char fragment_shader[] = - "varying highp vec2 uv;" - "uniform sampler2D textureSampler;" - "uniform bool swizzle;" - "uniform highp float opacity;" + "varying highp vec2 uv;\n" + "uniform sampler2D textureSampler;\n" + "uniform bool swizzle;\n" + "uniform highp float opacity;\n" + "#if defined SCRGB_TO_SRGB\n" + "highp vec4 linearToSRGB(highp vec4 value)\n" + "{\n" + " bvec4 cutoff = lessThan(value, vec4(0.0031308));\n" + " const highp vec2 a1 = vec2(0.055, 0.0);\n" + " const highp vec2 c2 = vec2(1.055, 1.0);\n" + " const highp vec2 m3 = vec2(2.4, 1.0);\n" + " const highp vec2 c4 = vec2(12.92, 1.0);\n" + " highp vec4 higher = c2.xxxy * pow(value, 1.0 / m3.xxxy) - a1.xxxy;\n" + " highp vec4 lower = value * c4.xxxy;\n" + " return mix(higher, lower, vec4(cutoff));\n" + "}\n" + "#endif\n" + "#if defined SRGB_TO_SCRGB || defined SRGB_TO_BT2020PQ || defined SCRGB_TO_BT2020PQ\n" + "highp vec4 sRgbToLinear(highp vec4 sRGB)\n" + "{\n" + " bvec4 cutoff = lessThan(sRGB, vec4(0.04045));\n" + " const highp vec2 a1 = vec2(0.055, 0.0);\n" + " const highp vec2 c2 = vec2(1.055, 1.0);\n" + " const highp vec2 m3 = vec2(2.4, 1.0);\n" + " const highp vec2 c4 = vec2(12.92, 1.0);\n" + " highp vec4 higher = pow((sRGB + a1.xxxy) / c2.xxxy, m3.xxxy);\n" + " highp vec4 lower = sRGB / c4.xxxy;\n" + " return mix(higher, lower, vec4(cutoff));\n" + "}\n" + "#endif\n" + "#if defined SRGB_TO_BT2020PQ || defined SCRGB_TO_BT2020PQ\n" + "highp vec4 applySmpte2084Curve(highp vec4 L)\n" + "{" + " const highp vec2 m1 = vec2(2610.0 / 4096.0 / 4.0, 1.0);\n" + " const highp vec2 m2 = vec2(2523.0 / 4096.0 * 128.0, 1.0);\n" + " const highp vec2 a1 = vec2(3424.0 / 4096.0, 0.0);\n" + " const highp vec2 c2 = vec2(2413.0 / 4096.0 * 32.0, 1.0);\n" + " const highp vec2 c3 = vec2(2392.0 / 4096.0 * 32.0, 1.0);\n" + " const highp vec2 a4 = vec2(1.0, 0.0);\n" + " highp vec4 Lp = pow(L, m1.xxxy);\n" + " highp vec4 res = pow((a1.xxxy + c2.xxxy * Lp) / (a4.xxxy + c3.xxxy * Lp), m2.xxxy);\n" + " return res;" + "}\n" + "#endif\n" + "#if defined SRGB_TO_BT2020PQ || defined SCRGB_TO_BT2020PQ\n" + "highp vec4 scRgbToBt2020pq(highp vec4 value)\n" + "{\n" + " const highp mat4 convMat = " + " mat4(0.627402, 0.069095, 0.016394, 0.0," + " 0.329292, 0.919544, 0.088028, 0.0," + " 0.043306, 0.011360, 0.895578, 0.0," + " 0.0, 0.0, 0.0, 1.0);" + "" + " value = convMat * value;\n" + " return applySmpte2084Curve(0.008 * value);" + "}\n" + "#endif\n" + "#if defined SRGB_TO_BT2020PQ\n" + "highp vec4 sRgbToBt2020pq(highp vec4 value)\n" + "{\n" + " value = sRgbToLinear(value);" + " return scRgbToBt2020pq(value);" + "}\n" + "#endif\n" + "\n" "void main() {" " highp vec4 tmpFragColor = texture2D(textureSampler,uv);" - " tmpFragColor.a *= opacity;" - " gl_FragColor = swizzle ? tmpFragColor.bgra : tmpFragColor;" + " tmpFragColor.a *= opacity;\n" + " tmpFragColor = swizzle ? tmpFragColor.bgra : tmpFragColor;\n" + "#if defined SRGB_TO_SCRGB\n" + " tmpFragColor = sRgbToLinear(tmpFragColor);\n" + "#elif defined SRGB_TO_BT2020PQ\n" + " tmpFragColor = sRgbToBt2020pq(tmpFragColor);\n" + "#elif defined SCRGB_TO_BT2020PQ\n" + " tmpFragColor = scRgbToBt2020pq(tmpFragColor);\n" + "#elif defined SCRGB_TO_SRGB\n" + " tmpFragColor = linearToSRGB(tmpFragColor);\n" + "#endif\n" + " gl_FragColor = tmpFragColor;" "}"; static const char fragment_shader_external_oes[] = @@ -187,6 +258,23 @@ private: GLenum m_target; }; +class ColorSpaceConversion : public QPair +{ +public: + ColorSpaceConversion() { }; + ColorSpaceConversion(QSurfaceFormat::ColorSpace srcColorSpace, + QSurfaceFormat::ColorSpace dstColorSpace) + : QPair(srcColorSpace, dstColorSpace) + { } + + QSurfaceFormat::ColorSpace source() const { + return first; + } + QSurfaceFormat::ColorSpace destination() const { + return second; + } +}; + class QOpenGLTextureBlitterPrivate { public: @@ -197,16 +285,29 @@ public: }; enum ProgramIndex { - TEXTURE_2D, - TEXTURE_EXTERNAL_OES + TEXTURE_2D = 0, + TEXTURE_2D_SRGB_TO_SCRGB, + TEXTURE_2D_SCRGB_TO_SRGB, + TEXTURE_2D_SRGB_TO_BT2020PQ, + TEXTURE_2D_SCRGB_TO_BT2020PQ, + TEXTURE_EXTERNAL_OES, + + PROGRAM_COUNT }; QOpenGLTextureBlitterPrivate() : swizzle(false), opacity(1.0f), vao(new QOpenGLVertexArrayObject), - currentTarget(TEXTURE_2D) - { } + currentTarget(GL_NONE), + colorSpaceConversion(0) + { + supportedColorSpaceConversions << ColorSpaceConversion(QSurfaceFormat::DefaultColorSpace, QSurfaceFormat::DefaultColorSpace); + supportedColorSpaceConversions << ColorSpaceConversion(QSurfaceFormat::sRGBColorSpace, QSurfaceFormat::scRGBColorSpace); + supportedColorSpaceConversions << ColorSpaceConversion(QSurfaceFormat::scRGBColorSpace, QSurfaceFormat::sRGBColorSpace); + supportedColorSpaceConversions << ColorSpaceConversion(QSurfaceFormat::sRGBColorSpace, QSurfaceFormat::bt2020PQColorSpace); + supportedColorSpaceConversions << ColorSpaceConversion(QSurfaceFormat::scRGBColorSpace, QSurfaceFormat::bt2020PQColorSpace); + } bool buildProgram(ProgramIndex idx, const char *vs, const char *fs); @@ -214,6 +315,7 @@ public: void blit(GLuint texture, const QMatrix4x4 &vertexTransform, QOpenGLTextureBlitter::Origin origin); void prepareProgram(const QMatrix4x4 &vertexTransform); + int calcColorSpaceConversionIndex(QSurfaceFormat::ColorSpace srcColorSpace, QSurfaceFormat::ColorSpace dstColorSpace); QOpenGLBuffer vertexBuffer; QOpenGLBuffer textureBuffer; @@ -239,18 +341,48 @@ public: bool swizzle; float opacity; TextureMatrixUniform textureMatrixUniformState; - } programs[2]; + } programs[PROGRAM_COUNT]; bool swizzle; float opacity; QScopedPointer vao; GLenum currentTarget; + + int colorSpaceConversion; + QVector supportedColorSpaceConversions; }; -static inline QOpenGLTextureBlitterPrivate::ProgramIndex targetToProgramIndex(GLenum target) +int QOpenGLTextureBlitterPrivate::calcColorSpaceConversionIndex(QSurfaceFormat::ColorSpace srcColorSpace, QSurfaceFormat::ColorSpace dstColorSpace) +{ + // TODO: auto-detect destination color space of the surface + // in case of default color space + + // disable color management if at least one of the color + // spaces is declared as default + if (srcColorSpace == QSurfaceFormat::DefaultColorSpace || + dstColorSpace == QSurfaceFormat::DefaultColorSpace) { + + return 0; + } + + // disable color management if source and destination color + // spaces are the same + if (srcColorSpace == dstColorSpace) { + return 0; + } + + ColorSpaceConversion conversion(srcColorSpace, dstColorSpace); + return supportedColorSpaceConversions.indexOf(conversion); +} + +static inline QOpenGLTextureBlitterPrivate::ProgramIndex targetToProgramIndex(GLenum target, int colorSpaceConversion) { switch (target) { - case GL_TEXTURE_2D: - return QOpenGLTextureBlitterPrivate::TEXTURE_2D; + case GL_TEXTURE_2D: { + QOpenGLTextureBlitterPrivate::ProgramIndex index = + QOpenGLTextureBlitterPrivate::ProgramIndex( + int(QOpenGLTextureBlitterPrivate::TEXTURE_2D) + colorSpaceConversion); + return index; + } case GL_TEXTURE_EXTERNAL_OES: return QOpenGLTextureBlitterPrivate::TEXTURE_EXTERNAL_OES; default: @@ -261,7 +393,7 @@ static inline QOpenGLTextureBlitterPrivate::ProgramIndex targetToProgramIndex(GL void QOpenGLTextureBlitterPrivate::prepareProgram(const QMatrix4x4 &vertexTransform) { - Program *program = &programs[targetToProgramIndex(currentTarget)]; + Program *program = &programs[targetToProgramIndex(currentTarget, colorSpaceConversion)]; vertexBuffer.bind(); program->glProgram->setAttributeBuffer(program->vertexCoordAttribPos, GL_FLOAT, 0, 3, 0); @@ -293,7 +425,7 @@ void QOpenGLTextureBlitterPrivate::blit(GLuint texture, TextureBinder binder(currentTarget, texture); prepareProgram(vertexTransform); - Program *program = &programs[targetToProgramIndex(currentTarget)]; + Program *program = &programs[targetToProgramIndex(currentTarget, colorSpaceConversion)]; program->glProgram->setUniformValue(program->textureTransformUniformPos, textureTransform); program->textureMatrixUniformState = User; @@ -307,7 +439,7 @@ void QOpenGLTextureBlitterPrivate::blit(GLuint texture, TextureBinder binder(currentTarget, texture); prepareProgram(vertexTransform); - Program *program = &programs[targetToProgramIndex(currentTarget)]; + Program *program = &programs[targetToProgramIndex(currentTarget, colorSpaceConversion)]; if (origin == QOpenGLTextureBlitter::OriginTopLeft) { if (program->textureMatrixUniformState != IdentityFlipped) { QMatrix3x3 flipped; @@ -408,6 +540,28 @@ bool QOpenGLTextureBlitter::create() } else { if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_2D, vertex_shader, fragment_shader)) return false; + + // TODO: create non-default transformations on-demand + { + const QString shader = QString("#define SRGB_TO_SCRGB\n %1").arg(fragment_shader); + if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_2D_SRGB_TO_SCRGB, vertex_shader, shader.toLatin1().constData())) + return false; + } + { + const QString shader = QString("#define SCRGB_TO_SRGB\n %1").arg(fragment_shader); + if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_2D_SCRGB_TO_SRGB, vertex_shader, shader.toLatin1().constData())) + return false; + } + { + const QString shader = QString("#define SRGB_TO_BT2020PQ\n %1").arg(fragment_shader); + if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_2D_SRGB_TO_BT2020PQ, vertex_shader, shader.toLatin1().constData())) + return false; + } + { + const QString shader = QString("#define SCRGB_TO_BT2020PQ\n %1").arg(fragment_shader); + if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_2D_SCRGB_TO_BT2020PQ, vertex_shader, shader.toLatin1().constData())) + return false; + } if (supportsExternalOESTarget()) if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_EXTERNAL_OES, vertex_shader, fragment_shader_external_oes)) return false; @@ -455,6 +609,8 @@ void QOpenGLTextureBlitter::destroy() return; Q_D(QOpenGLTextureBlitter); d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_2D].glProgram.reset(); + d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_2D_SRGB_TO_SCRGB].glProgram.reset(); + d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_2D_SRGB_TO_BT2020PQ].glProgram.reset(); d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_EXTERNAL_OES].glProgram.reset(); d->vertexBuffer.destroy(); d->textureBuffer.destroy(); @@ -484,15 +640,26 @@ bool QOpenGLTextureBlitter::supportsExternalOESTarget() const \sa release(), blit() */ -void QOpenGLTextureBlitter::bind(GLenum target) +void QOpenGLTextureBlitter::bind(GLenum target, + QSurfaceFormat::ColorSpace srcColorSpace, + QSurfaceFormat::ColorSpace dstColorSpace) { Q_D(QOpenGLTextureBlitter); if (d->vao->isCreated()) d->vao->bind(); + const int index = d->calcColorSpaceConversionIndex(srcColorSpace, dstColorSpace); + + if (index >= 0) { + d->colorSpaceConversion = index; + } else { + qWarning() << "QOpenGLTextureBlitter::bind(): color space conversion is not supported" << srcColorSpace << dstColorSpace; + d->colorSpaceConversion = 0; // noop conversion + } + d->currentTarget = target; - QOpenGLTextureBlitterPrivate::Program *p = &d->programs[targetToProgramIndex(target)]; + QOpenGLTextureBlitterPrivate::Program *p = &d->programs[targetToProgramIndex(target, d->colorSpaceConversion)]; p->glProgram->bind(); d->vertexBuffer.bind(); @@ -506,6 +673,21 @@ void QOpenGLTextureBlitter::bind(GLenum target) d->textureBuffer.release(); } +void QOpenGLTextureBlitter::rebind(GLenum target, QSurfaceFormat::ColorSpace srcColorSpace, QSurfaceFormat::ColorSpace dstColorSpace) +{ + Q_D(QOpenGLTextureBlitter); + + if (d->vao->isCreated() && + d->currentTarget == target && + d->colorSpaceConversion == d->calcColorSpaceConversionIndex(srcColorSpace, dstColorSpace)) { + + // the blitter is already configured in the correct state, so just skip it + return; + } + + bind(target, srcColorSpace, dstColorSpace); +} + /*! Unbinds the graphics resources used by the blitter. @@ -514,7 +696,7 @@ void QOpenGLTextureBlitter::bind(GLenum target) void QOpenGLTextureBlitter::release() { Q_D(QOpenGLTextureBlitter); - d->programs[targetToProgramIndex(d->currentTarget)].glProgram->release(); + d->programs[targetToProgramIndex(d->currentTarget, d->colorSpaceConversion)].glProgram->release(); if (d->vao->isCreated()) d->vao->release(); } diff --git a/src/gui/opengl/qopengltextureblitter.h b/src/gui/opengl/qopengltextureblitter.h index 2f7c6b1a0..3c87e4e2b 100644 --- a/src/gui/opengl/qopengltextureblitter.h +++ b/src/gui/opengl/qopengltextureblitter.h @@ -48,6 +48,9 @@ #include #include +// TODO: less includes!!! +#include + QT_BEGIN_NAMESPACE class QOpenGLTextureBlitterPrivate; @@ -69,7 +72,14 @@ public: bool supportsExternalOESTarget() const; - void bind(GLenum target = GL_TEXTURE_2D); + void bind(GLenum target = GL_TEXTURE_2D, + QSurfaceFormat::ColorSpace srcColorSpace = QSurfaceFormat::DefaultColorSpace, + QSurfaceFormat::ColorSpace dstColorSpace = QSurfaceFormat::DefaultColorSpace); + + void rebind(GLenum target = GL_TEXTURE_2D, + QSurfaceFormat::ColorSpace srcColorSpace = QSurfaceFormat::DefaultColorSpace, + QSurfaceFormat::ColorSpace dstColorSpace = QSurfaceFormat::DefaultColorSpace); + void release(); void setRedBlueSwizzle(bool swizzle); diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp index c71d82546..8dd96a66b 100644 --- a/src/gui/painting/qplatformbackingstore.cpp +++ b/src/gui/painting/qplatformbackingstore.cpp @@ -132,6 +132,7 @@ struct QBackingstoreTextureInfo QRect rect; QRect clipRect; QPlatformTextureList::Flags flags; + QSurfaceFormat::ColorSpace colorSpace; }; Q_DECLARE_TYPEINFO(QBackingstoreTextureInfo, Q_MOVABLE_TYPE); @@ -181,6 +182,12 @@ QPlatformTextureList::Flags QPlatformTextureList::flags(int index) const return d->textures.at(index).flags; } +QSurfaceFormat::ColorSpace QPlatformTextureList::colorSpace(int index) const +{ + Q_D(const QPlatformTextureList); + return d->textures.at(index).colorSpace; +} + QRect QPlatformTextureList::geometry(int index) const { Q_D(const QPlatformTextureList); @@ -209,7 +216,7 @@ bool QPlatformTextureList::isLocked() const } void QPlatformTextureList::appendTexture(void *source, GLuint textureId, const QRect &geometry, - const QRect &clipRect, Flags flags) + const QRect &clipRect, Flags flags, QSurfaceFormat::ColorSpace colorSpace) { Q_D(QPlatformTextureList); QBackingstoreTextureInfo bi; @@ -218,6 +225,7 @@ void QPlatformTextureList::appendTexture(void *source, GLuint textureId, const Q bi.rect = geometry; bi.clipRect = clipRect; bi.flags = flags; + bi.colorSpace = colorSpace; d->textures.append(bi); } @@ -300,6 +308,7 @@ static void blitTextureForWidget(const QPlatformTextureList *textures, int idx, if (srgb && canUseSrgb) funcs->glEnable(GL_FRAMEBUFFER_SRGB); + blitter->rebind(GL_TEXTURE_2D, textures->colorSpace(idx), window->format().colorSpace()); blitter->blit(textures->textureId(idx), target, source); if (srgb && canUseSrgb) @@ -433,6 +442,11 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i funcs->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); if (textureId) { + // GUI texture is always in sRGB color space + d_ptr->blitter->rebind(GL_TEXTURE_2D, + QSurfaceFormat::sRGBColorSpace, + window->format().colorSpace()); + if (d_ptr->needsSwizzle) d_ptr->blitter->setRedBlueSwizzle(true); // The backingstore is for the entire tlw. diff --git a/src/gui/painting/qplatformbackingstore.h b/src/gui/painting/qplatformbackingstore.h index de5ba964d..f8887bd4c 100644 --- a/src/gui/painting/qplatformbackingstore.h +++ b/src/gui/painting/qplatformbackingstore.h @@ -95,11 +95,13 @@ public: QRect clipRect(int index) const; void *source(int index); Flags flags(int index) const; + QSurfaceFormat::ColorSpace colorSpace(int index) const; void lock(bool on); bool isLocked() const; void appendTexture(void *source, GLuint textureId, const QRect &geometry, - const QRect &clipRect = QRect(), Flags flags = 0); + const QRect &clipRect = QRect(), Flags flags = 0, + QSurfaceFormat::ColorSpace colorSpace = QSurfaceFormat::DefaultColorSpace); void clear(); Q_SIGNALS: diff --git a/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp b/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp index 40400e2a1..5d44e6245 100644 --- a/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp +++ b/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp @@ -230,7 +230,7 @@ void QOpenGLCompositorBackingStore::composeAndFlush(QWindow *window, const QRegi m_textures->clear(); for (int i = 0; i < textures->count(); ++i) m_textures->appendTexture(textures->source(i), textures->textureId(i), textures->geometry(i), - textures->clipRect(i), textures->flags(i)); + textures->clipRect(i), textures->flags(i), textures->colorSpace(i)); updateTexture(); m_textures->appendTexture(nullptr, m_bsTexture, window->geometry()); diff --git a/src/plugins/platforms/windows/qwindowseglcontext.cpp b/src/plugins/platforms/windows/qwindowseglcontext.cpp index 063e81150..4cd745eac 100644 --- a/src/plugins/platforms/windows/qwindowseglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowseglcontext.cpp @@ -151,8 +151,9 @@ bool QWindowsLibEGL::init() eglGetCurrentDisplay = RESOLVE((EGLDisplay (EGLAPIENTRY *)(void)), eglGetCurrentDisplay); eglSwapBuffers = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay , EGLSurface)), eglSwapBuffers); eglGetProcAddress = RESOLVE((QFunctionPointer (EGLAPIENTRY * )(const char *)), eglGetProcAddress); + eglQueryString = RESOLVE((const char* (EGLAPIENTRY *)(EGLDisplay, EGLint)), eglQueryString); - if (!eglGetError || !eglGetDisplay || !eglInitialize || !eglGetProcAddress) + if (!eglGetError || !eglGetDisplay || !eglInitialize || !eglGetProcAddress || !eglQueryString) return false; eglGetPlatformDisplayEXT = nullptr; @@ -197,8 +198,15 @@ bool QWindowsLibGLESv2::init() } QWindowsEGLStaticContext::QWindowsEGLStaticContext(EGLDisplay display) - : m_display(display) + : m_display(display), + m_hasSRGBColorSpaceSupport(false), + m_hasSCRGBColorSpaceSupport(false), + m_hasBt2020PQColorSpaceSupport(false) { + const char *eglExtensions = libEGL.eglQueryString(display, EGL_EXTENSIONS); + m_hasSRGBColorSpaceSupport = strstr(eglExtensions, "EGL_KHR_gl_colorspace") != nullptr; + m_hasSCRGBColorSpaceSupport = strstr(eglExtensions, "EGL_EXT_gl_colorspace_scrgb_linear") != nullptr; + m_hasBt2020PQColorSpaceSupport = strstr(eglExtensions, "EGL_EXT_gl_colorspace_bt2020_pq") != nullptr; } bool QWindowsEGLStaticContext::initializeAngle(QWindowsOpenGLTester::Renderers preferredType, HDC dc, @@ -297,11 +305,48 @@ QWindowsOpenGLContext *QWindowsEGLStaticContext::createContext(QOpenGLContext *c return new QWindowsEGLContext(this, context->format(), context->shareHandle()); } -void *QWindowsEGLStaticContext::createWindowSurface(void *nativeWindow, void *nativeConfig, int *err) +void *QWindowsEGLStaticContext::createWindowSurface(void *nativeWindow, void *nativeConfig, + QSurfaceFormat::ColorSpace colorSpace, int *err) { *err = 0; + + EGLint eglColorSpace = EGL_GL_COLORSPACE_LINEAR_KHR; + bool colorSpaceSupported = false; + + switch (colorSpace) { + case QSurfaceFormat::DefaultColorSpace: + colorSpaceSupported = m_hasSRGBColorSpaceSupport; + break; + case QSurfaceFormat::sRGBColorSpace: + eglColorSpace = EGL_GL_COLORSPACE_SRGB_KHR; + colorSpaceSupported = m_hasSRGBColorSpaceSupport; + break; + case QSurfaceFormat::scRGBColorSpace: + eglColorSpace = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT; + colorSpaceSupported = m_hasSCRGBColorSpaceSupport; + break; + case QSurfaceFormat::bt2020PQColorSpace: + eglColorSpace = EGL_GL_COLORSPACE_BT2020_PQ_EXT; + colorSpaceSupported = m_hasBt2020PQColorSpaceSupport; + break; + } + + QVector attributes; + + if (colorSpaceSupported) { + attributes << EGL_GL_COLORSPACE << eglColorSpace; + } + + attributes << EGL_NONE; + + if (!colorSpaceSupported && colorSpace != QSurfaceFormat::DefaultColorSpace) { + qWarning().nospace() << __FUNCTION__ << ": Requested color space is not supported by EGL implementation: " << colorSpace << " (egl: 0x" << hex << eglColorSpace << ")"; + } + + EGLSurface surface = libEGL.eglCreateWindowSurface(m_display, nativeConfig, - static_cast(nativeWindow), nullptr); + static_cast(nativeWindow), + attributes.constData()); if (surface == EGL_NO_SURFACE) { *err = libEGL.eglGetError(); qWarning("%s: Could not create the EGL window surface: 0x%x", __FUNCTION__, *err); @@ -349,6 +394,7 @@ QSurfaceFormat QWindowsEGLStaticContext::formatFromConfig(EGLDisplay display, EG format.setSamples(sampleCount); format.setStereo(false); format.setSwapInterval(referenceFormat.swapInterval()); + format.setColorSpace(referenceFormat.colorSpace()); // Clear the EGL error state because some of the above may // have errored out because the attribute is not applicable @@ -378,7 +424,6 @@ QSurfaceFormat QWindowsEGLStaticContext::formatFromConfig(EGLDisplay display, EG \internal \ingroup qt-lighthouse-win */ - QWindowsEGLContext::QWindowsEGLContext(QWindowsEGLStaticContext *staticContext, const QSurfaceFormat &format, QPlatformOpenGLContext *share) @@ -483,6 +528,8 @@ bool QWindowsEGLContext::makeCurrent(QPlatformSurface *surface) // Simulate context loss as the context is useless. QWindowsEGLStaticContext::libEGL.eglDestroyContext(m_eglDisplay, m_eglContext); m_eglContext = EGL_NO_CONTEXT; + } else if (err == EGL_BAD_MATCH) { + qCDebug(lcQpaGl) << "Got bad match in createWindowSurface() for context" << this << "Check color space configuration."; } return false; } diff --git a/src/plugins/platforms/windows/qwindowseglcontext.h b/src/plugins/platforms/windows/qwindowseglcontext.h index 8a1e1ddae..9f7742e6f 100644 --- a/src/plugins/platforms/windows/qwindowseglcontext.h +++ b/src/plugins/platforms/windows/qwindowseglcontext.h @@ -80,6 +80,7 @@ struct QWindowsLibEGL QFunctionPointer (EGLAPIENTRY *eglGetProcAddress)(const char *procname); EGLDisplay (EGLAPIENTRY * eglGetPlatformDisplayEXT)(EGLenum platform, void *native_display, const EGLint *attrib_list); + const char* (EGLAPIENTRY * eglQueryString)(EGLDisplay dpy, EGLint name); private: #if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC) @@ -121,7 +122,7 @@ public: void *moduleHandle() const override { return libGLESv2.moduleHandle(); } QOpenGLContext::OpenGLModuleType moduleType() const override { return QOpenGLContext::LibGLES; } - void *createWindowSurface(void *nativeWindow, void *nativeConfig, int *err) override; + void *createWindowSurface(void *nativeWindow, void *nativeConfig, QSurfaceFormat::ColorSpace colorSpace, int *err) override; void destroyWindowSurface(void *nativeSurface) override; QSurfaceFormat formatFromConfig(EGLDisplay display, EGLConfig config, const QSurfaceFormat &referenceFormat); @@ -135,6 +136,9 @@ private: EGLDisplay *display, EGLint *major, EGLint *minor); const EGLDisplay m_display; + bool m_hasSRGBColorSpaceSupport; + bool m_hasSCRGBColorSpaceSupport; + bool m_hasBt2020PQColorSpaceSupport; }; class QWindowsEGLContext : public QWindowsOpenGLContext diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp index ed945ec4b..1c5be4415 100644 --- a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp +++ b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp @@ -40,6 +40,7 @@ #include "qwindowsnativeinterface.h" #include "qwindowsclipboard.h" #include "qwindowswindow.h" +#include "qwindowsscreen.h" #include "qwindowscontext.h" #include "qwindowscursor.h" #include "qwindowsopenglcontext.h" @@ -124,6 +125,21 @@ void *QWindowsNativeInterface::nativeResourceForWindow(const QByteArray &resourc return nullptr; } +void *QWindowsNativeInterface::nativeResourceForScreen(const QByteArray &resource, QScreen *screen) +{ + if (!screen || !screen->handle()) { + qWarning("%s: '%s' requested for null screen or screen without handle.", __FUNCTION__, resource.constData()); + return 0; + } + QWindowsScreen *bs = static_cast(screen->handle()); + int type = resourceType(resource); + if (type == HandleType) + return bs->handle(); + + qWarning("%s: Invalid key '%s' requested.", __FUNCTION__, resource.constData()); + return 0; +} + #ifndef QT_NO_CURSOR void *QWindowsNativeInterface::nativeResourceForCursor(const QByteArray &resource, const QCursor &cursor) { diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.h b/src/plugins/platforms/windows/qwindowsnativeinterface.h index e6f8aae8f..ce395dc5a 100644 --- a/src/plugins/platforms/windows/qwindowsnativeinterface.h +++ b/src/plugins/platforms/windows/qwindowsnativeinterface.h @@ -74,6 +74,7 @@ public: void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) override; #endif void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override; + void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen) override; #ifndef QT_NO_CURSOR void *nativeResourceForCursor(const QByteArray &resource, const QCursor &cursor) override; #endif diff --git a/src/plugins/platforms/windows/qwindowsopenglcontext.h b/src/plugins/platforms/windows/qwindowsopenglcontext.h index cc6d93d35..61c0e2876 100644 --- a/src/plugins/platforms/windows/qwindowsopenglcontext.h +++ b/src/plugins/platforms/windows/qwindowsopenglcontext.h @@ -63,7 +63,7 @@ public: // If the windowing system interface needs explicitly created window surfaces (like EGL), // reimplement these. - virtual void *createWindowSurface(void * /*nativeWindow*/, void * /*nativeConfig*/, int * /*err*/) { return 0; } + virtual void *createWindowSurface(void * /*nativeWindow*/, void * /*nativeConfig*/, QSurfaceFormat::ColorSpace /*colorSpace*/, int * /*err*/) { return 0; } virtual void destroyWindowSurface(void * /*nativeSurface*/) { } protected: diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 0520f8893..b70b0bbe3 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -321,6 +321,11 @@ void QWindowsScreen::handleChanges(const QWindowsScreenData &newData) } } +HMONITOR QWindowsScreen::handle() const +{ + return m_data.hMonitor; +} + QRect QWindowsScreen::virtualGeometry(const QPlatformScreen *screen) // cf QScreen::virtualGeometry() { QRect result; diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h index 824bcb1ad..33c9effa2 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.h +++ b/src/plugins/platforms/windows/qwindowsscreen.h @@ -104,6 +104,8 @@ public: inline void handleChanges(const QWindowsScreenData &newData); + HMONITOR handle() const; + #ifndef QT_NO_CURSOR QPlatformCursor *cursor() const override { return m_cursor.data(); } const CursorPtr &cursorPtr() const { return m_cursor; } diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 0376e363f..76a443a89 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -2741,9 +2741,13 @@ void *QWindowsWindow::surface(void *nativeConfig, int *err) return 0; #endif #ifndef QT_NO_OPENGL + + + if (!m_surface) { - if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext()) - m_surface = staticOpenGLContext->createWindowSurface(m_data.hwnd, nativeConfig, err); + if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext()) { + m_surface = staticOpenGLContext->createWindowSurface(m_data.hwnd, nativeConfig, m_format.colorSpace(), err); + } } return m_surface; diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp index 89f860150..0abf707e0 100644 --- a/src/widgets/kernel/qopenglwidget.cpp +++ b/src/widgets/kernel/qopenglwidget.cpp @@ -568,7 +568,8 @@ public: updateBehavior(QOpenGLWidget::NoPartialUpdate), requestedSamples(0), inPaintGL(false), - textureFormat(0) + textureFormat(0), + textureColorSpace(QSurfaceFormat::DefaultColorSpace) { requestedFormat = QSurfaceFormat::defaultFormat(); } @@ -578,6 +579,7 @@ public: GLuint textureId() const override; QPlatformTextureList::Flags textureListFlags() override; + QSurfaceFormat::ColorSpace colorSpace() const override; void initialize(); void invokeUserPaint(); @@ -609,6 +611,7 @@ public: int requestedSamples; bool inPaintGL; GLenum textureFormat; + QSurfaceFormat::ColorSpace textureColorSpace; }; void QOpenGLWidgetPaintDevicePrivate::beginPaint() @@ -695,6 +698,11 @@ QPlatformTextureList::Flags QOpenGLWidgetPrivate::textureListFlags() return flags; } +QSurfaceFormat::ColorSpace QOpenGLWidgetPrivate::colorSpace() const +{ + return textureColorSpace; +} + void QOpenGLWidgetPrivate::reset() { Q_Q(QOpenGLWidget); @@ -1115,6 +1123,41 @@ void QOpenGLWidget::setTextureFormat(GLenum texFormat) d->textureFormat = texFormat; } +/*! + \return the declared color space of the internal texture of the widget. + + The texture's color space will be used when composing the widget + into the root window surface. + + \note when the color space is set to QSurfaceFormat::DefaultColorSpace, + color conversion is effectively disabled. + + \since 5.99 + */ +QSurfaceFormat::ColorSpace QOpenGLWidget::textureColorSpace() const +{ + Q_D(const QOpenGLWidget); + return d->textureColorSpace; +} + +/*! + Sets a custom color space for the internal texture of the widget + + The color space of the texture will be compared against the color + space of the root surface and conversion will be performed if needed. + + \note setting the color space to QSurfaceFormat::DefaultColorSpace will + effectively disable color conversion when composing this texture on + screen. + + \since 5.99 + */ +void QOpenGLWidget::setTextureColorSpace(QSurfaceFormat::ColorSpace colorSpace) +{ + Q_D(QOpenGLWidget); + d->textureColorSpace = colorSpace; +} + /*! \return the active internal texture format if the widget has already initialized, the requested format if one was set but the widget has not yet diff --git a/src/widgets/kernel/qopenglwidget.h b/src/widgets/kernel/qopenglwidget.h index 9eb4a9ba5..eff2d9796 100644 --- a/src/widgets/kernel/qopenglwidget.h +++ b/src/widgets/kernel/qopenglwidget.h @@ -75,6 +75,9 @@ public: GLenum textureFormat() const; void setTextureFormat(GLenum texFormat); + QSurfaceFormat::ColorSpace textureColorSpace() const; + void setTextureColorSpace(QSurfaceFormat::ColorSpace colorSpace); + bool isValid() const; void makeCurrent(); diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index 7e4ea2cc0..1ff5af426 100644 --- a/src/widgets/kernel/qwidget_p.h +++ b/src/widgets/kernel/qwidget_p.h @@ -655,6 +655,7 @@ public: ? QPlatformTextureList::StacksOnTop : QPlatformTextureList::Flags(0); } + virtual QSurfaceFormat::ColorSpace colorSpace() const { return QSurfaceFormat::DefaultColorSpace; } virtual QImage grabFramebuffer() { return QImage(); } virtual void beginBackingStorePainting() { } virtual void endBackingStorePainting() { } diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp index a32eb2a03..db6033803 100644 --- a/src/widgets/kernel/qwidgetbackingstore.cpp +++ b/src/widgets/kernel/qwidgetbackingstore.cpp @@ -1007,7 +1007,7 @@ static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatfo if (wd->renderToTexture) { QPlatformTextureList::Flags flags = wd->textureListFlags(); const QRect rect(widget->mapTo(tlw, QPoint()), widget->size()); - widgetTextures->appendTexture(widget, wd->textureId(), rect, wd->clipRect(), flags); + widgetTextures->appendTexture(widget, wd->textureId(), rect, wd->clipRect(), flags, wd->colorSpace()); } for (int i = 0; i < wd->children.size(); ++i) { diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp index 6c871aae2..19fc2d167 100644 --- a/src/widgets/widgets/qdockwidget.cpp +++ b/src/widgets/widgets/qdockwidget.cpp @@ -1171,6 +1171,8 @@ void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect QMainWindowLayout *mwlayout = qt_mainwindow_layout_from_dock(q); if (mwlayout) emit q->dockLocationChanged(mwlayout->dockWidgetArea(q)); + } else { + emit q->dockLocationChanged(Qt::NoDockWidgetArea); } } diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro index ab00a5ef6..b202ed043 100644 --- a/tests/manual/manual.pro +++ b/tests/manual/manual.pro @@ -59,7 +59,7 @@ qtabbar qtConfig(opengl) { SUBDIRS += qopengltextureblitter - qtConfig(egl): SUBDIRS += qopenglcontext + qtConfig(egl): SUBDIRS += qopenglcontext hdr-qopenglwidget } win32: SUBDIRS -= network_remote_stresstest network_stresstest