From 76c2cfb90671dda63ec29e4f5ff8ae166606eb81 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Thu, 16 May 2024 23:59:56 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1232902. Update ANGLE to chromium/2592 (89d3d3cfad) - Bug 1251375. Update to ANGLE/2653 (98691dfd84) - bits of Bug 1225280. Update ANGLE to chromium/2572. (b5eeadcb4e) - Bug 1251375 - Cross compilation fixup. r=upstream (737d8f8554) - rename back (e1ef46b16a) - Bug 1261129: Make VP9 sample data const. r=kentuckyfriedtakahe (811eead31b) - Bug 1263839 - WebM is now actually a VP9 sample; r=me (c6568d6d98) - Bug 1263839: P2. Force re-run of VP9 benchmark based on a version check. r=kentuckyfriedtakahe (358409a235) - Bug 1235503 - Fix -Wunreachable-code warnings in dom/media/. r=jya (dc6bebb141) - Bug 1251184: [quicktime] P1. Report video/quicktime mimetype when sniffing. r=cpearce (056dda066a) - Bug 1251184: [quicktime] P2. Use external plugin if available over native playback. r=cpearce (6f34c09ab6) - Bug 1255050 - [1.1] Restrict media plugin decoder usage to Android ICS. r=snorp (ae801e040c) - Bug 1229657: [MSE] Returns NotSupportedError if mimetype is invalid or not supported. r=gerald (a81df7babf) - change errors returns (18b81f684e) - rearrange to match gecko code (333c4c5f3a) - Bug 1239607 - Let platform layer decide which codec to support and how to configure it. r=sotaro (63812a44d1) - Bug 1248507 - p1. Pass DecoderDoctorDiagnostics to PDMs&more - r=jya (4175551833) - Bug 1248507 - p2. DecoderDoctorDiagnostics boilerplate - r=jya (cb25b71956) - Bug 1208371 - Forward declare DOMMediaStream in HTMLMediaElement.h. r=jesup (896080a020) - Bug 1248507 - p3. Use DecoderDoctorDiagnostics - r=jya,bz (181966589e) - Bug 1248507 - p4. DecoderDoctor base console message - r=bz (c5704ad2fe) - Bug 1055776 - Move namespaceURI, prefix, localName from Node to Element; r=bz (ba7a18385d) - Bug 842818 - Expose WebCrypto API to workers r=baku (966e5f3e75) - Bug 1227790 - Update MediaKeyStatuses to include "released", "output-restricted" and "status-pending". r=bz (d9e7ddb298) - Bug 1256046 - Hide MozPowerManager from the Web; r=khuey,bzbarsky (ec1da24251) - Bug 1259581: Remove MOZ_MEDIA_NAVIGATOR. r=jesup (023a114462) - Bug 1254956 - Implement Node.rootNode. r=Ms2ger,smaug (0133a41059) - Bug 1264409 - Make last transaction ID available via nsIDOMWindowUtils, and pass transaction ID through MozAfterPaint. r=mattwoodrow,mrbkap (51184de1af) --- b2g/confvars.sh | 2 - browser/confvars.sh | 1 - configure.in | 13 - dom/base/Navigator.cpp | 8 - dom/base/Navigator.h | 6 - dom/base/nsDOMWindowUtils.cpp | 13 + dom/base/nsDocument.cpp | 4 - dom/base/nsINode.h | 5 + dom/base/test/test_bug352728.html | 8 +- dom/base/test/test_bug352728.xhtml | 8 +- dom/bindings/parser/WebIDL.py | 10 +- dom/canvas/WebGLShaderValidator.cpp | 2 +- dom/canvas/test/webgl-mochitest.ini | 2 +- dom/events/NotifyPaintEvent.cpp | 23 +- dom/events/NotifyPaintEvent.h | 9 +- dom/html/HTMLMediaElement.cpp | 45 +- dom/html/HTMLMediaElement.h | 6 +- .../html/dom/test_interfaces.html.json | 3 - dom/interfaces/base/nsIDOMWindowUtils.idl | 5 + .../events/nsIDOMNotifyPaintEvent.idl | 2 + dom/locales/en-US/chrome/dom/dom.properties | 4 + dom/media/ADTSDecoder.cpp | 3 +- dom/media/DecoderDoctorDiagnostics.cpp | 35 + dom/media/DecoderDoctorDiagnostics.h | 54 + dom/media/DecoderTraits.cpp | 78 +- dom/media/DecoderTraits.h | 16 +- dom/media/MP3Decoder.cpp | 3 +- dom/media/MediaDecoder.cpp | 10 +- dom/media/MediaDecoder.h | 2 +- dom/media/MediaFormatReader.cpp | 4 +- dom/media/fmp4/MP4Decoder.cpp | 16 +- dom/media/fmp4/MP4Decoder.h | 6 +- dom/media/gtest/TestMediaDataDecoder.cpp | 3 +- dom/media/mediasource/MediaSource.cpp | 66 +- dom/media/moz.build | 2 + dom/media/platforms/PDMFactory.cpp | 27 +- dom/media/platforms/PDMFactory.h | 10 +- dom/media/platforms/PlatformDecoderModule.h | 10 +- .../agnostic/AgnosticDecoderModule.cpp | 9 +- .../agnostic/AgnosticDecoderModule.h | 9 +- .../platforms/agnostic/BlankDecoderModule.cpp | 9 +- .../agnostic/gmp/GMPDecoderModule.cpp | 15 +- .../platforms/agnostic/gmp/GMPDecoderModule.h | 9 +- .../android/AndroidDecoderModule.cpp | 9 +- .../platforms/android/AndroidDecoderModule.h | 9 +- .../platforms/apple/AppleDecoderModule.cpp | 9 +- .../platforms/apple/AppleDecoderModule.h | 9 +- .../platforms/ffmpeg/FFmpegDecoderModule.h | 9 +- .../ffmpeg/{README.mcp => README.mozilla} | 0 .../platforms/gonk/GonkDecoderModule.cpp | 9 +- dom/media/platforms/gonk/GonkDecoderModule.h | 9 +- dom/media/platforms/omx/OmxPlatformLayer.cpp | 212 +++ dom/media/platforms/wmf/WMFDecoderModule.cpp | 9 +- dom/media/platforms/wmf/WMFDecoderModule.h | 9 +- .../platforms/wrappers/H264Converter.cpp | 12 +- dom/media/platforms/wrappers/H264Converter.h | 5 +- dom/media/wave/WaveDecoder.cpp | 3 +- .../mochitest/dom-level2-core/mochitest.ini | 2 - .../dom-level2-core/test_localName03.html | 127 -- .../dom-level2-core/test_prefix02.html | 128 -- .../mochitest/general/test_interfaces.html | 2 +- dom/webidl/Attr.webidl | 3 + dom/webidl/Crypto.webidl | 4 +- dom/webidl/DecoderDoctorNotification.webidl | 15 + dom/webidl/Element.webidl | 9 +- dom/webidl/MediaKeyStatusMap.webidl | 4 +- dom/webidl/MozPowerManager.webidl | 1 + dom/webidl/Navigator.webidl | 2 - dom/webidl/Node.webidl | 12 +- dom/webidl/NotifyPaintEvent.webidl | 2 + dom/webidl/SubtleCrypto.webidl | 1 + dom/webidl/WorkerGlobalScope.webidl | 1 + dom/workers/WorkerScope.cpp | 16 + dom/workers/WorkerScope.h | 5 + gfx/angle/BUILD.gn | 118 +- gfx/angle/DEPS | 36 +- gfx/angle/README.md | 9 +- gfx/angle/include/EGL/eglext.h | 40 + gfx/angle/include/GLES2/gl2ext.h | 15 + gfx/angle/include/GLSLANG/ShaderLang.h | 59 +- gfx/angle/include/GLSLANG/ShaderVars.h | 5 +- gfx/angle/moz.build | 25 +- gfx/angle/src/angle.gyp | 2 + gfx/angle/src/commit.h | 4 +- .../src/common/BitSetIterator_unittest.cpp | 2 +- gfx/angle/src/common/Optional.h | 7 + gfx/angle/src/common/angleutils.cpp | 7 + gfx/angle/src/common/angleutils.h | 1 + gfx/angle/src/common/debug.cpp | 2 +- gfx/angle/src/common/debug.h | 12 +- gfx/angle/src/common/mathutil.h | 12 +- gfx/angle/src/common/string_utils.cpp | 66 +- gfx/angle/src/common/string_utils.h | 23 +- .../src/common/string_utils_unittest.cpp | 111 +- gfx/angle/src/compiler.gypi | 74 +- .../compiler/translator/ASTMetadataHLSL.cpp | 6 +- gfx/angle/src/compiler/translator/CallDAG.cpp | 21 +- gfx/angle/src/compiler/translator/CodeGen.cpp | 27 +- gfx/angle/src/compiler/translator/Common.h | 23 +- .../src/compiler/translator/Compiler.cpp | 57 +- gfx/angle/src/compiler/translator/Compiler.h | 2 + .../compiler/translator/DirectiveHandler.cpp | 18 +- .../compiler/translator/DirectiveHandler.h | 9 +- .../src/compiler/translator/ForLoopUnroll.cpp | 29 +- .../src/compiler/translator/ForLoopUnroll.h | 6 +- .../src/compiler/translator/OutputHLSL.cpp | 909 +++++++--- .../src/compiler/translator/OutputHLSL.h | 27 +- .../src/compiler/translator/ParseContext.cpp | 119 +- .../src/compiler/translator/ParseContext.h | 19 +- .../src/compiler/translator/ShaderLang.cpp | 11 +- .../src/compiler/translator/ShaderVars.cpp | 5 + .../src/compiler/translator/SymbolTable.cpp | 2 +- .../src/compiler/translator/SymbolTable.h | 23 +- .../compiler/translator/TranslatorHLSL.cpp | 2 +- gfx/angle/src/compiler/translator/Types.cpp | 21 + .../src/compiler/translator/UniformHLSL.cpp | 134 +- .../src/compiler/translator/UniformHLSL.h | 12 +- .../src/compiler/translator/UtilsHLSL.cpp | 207 ++- gfx/angle/src/compiler/translator/UtilsHLSL.h | 42 +- .../translator/ValidateLimitations.cpp | 58 +- .../compiler/translator/ValidateLimitations.h | 8 +- .../src/compiler/translator/VariableInfo.cpp | 22 +- .../src/compiler/translator/blocklayout.h | 2 - .../compiler/translator/blocklayoutHLSL.cpp | 11 +- .../translator/depgraph/DependencyGraph.h | 24 +- .../depgraph/DependencyGraphOutput.cpp | 3 +- .../compiler/translator/generate_parser.sh | 11 +- gfx/angle/src/compiler/translator/glslang.y | 48 +- .../src/compiler/translator/glslang_tab.cpp | 90 +- .../src/compiler/translator/intermOut.cpp | 22 - .../timing/RestrictFragmentShaderTiming.cpp | 10 +- gfx/angle/src/libANGLE/BinaryStream.h | 12 +- .../src/libANGLE/BinaryStream_unittest.cpp | 71 + gfx/angle/src/libANGLE/Buffer.cpp | 11 + gfx/angle/src/libANGLE/Buffer.h | 9 +- gfx/angle/src/libANGLE/Caps.cpp | 70 +- gfx/angle/src/libANGLE/Caps.h | 60 +- gfx/angle/src/libANGLE/Config.cpp | 6 +- gfx/angle/src/libANGLE/Config.h | 1 + gfx/angle/src/libANGLE/Context.cpp | 884 +++++++-- gfx/angle/src/libANGLE/Context.h | 117 +- gfx/angle/src/libANGLE/Data.cpp | 8 +- gfx/angle/src/libANGLE/Data.h | 5 +- gfx/angle/src/libANGLE/Debug.cpp | 303 ++++ gfx/angle/src/libANGLE/Debug.h | 120 ++ gfx/angle/src/libANGLE/Device.cpp | 61 +- gfx/angle/src/libANGLE/Device.h | 13 +- gfx/angle/src/libANGLE/Display.cpp | 251 ++- gfx/angle/src/libANGLE/Display.h | 10 +- gfx/angle/src/libANGLE/Error.cpp | 35 +- gfx/angle/src/libANGLE/Error.h | 32 +- gfx/angle/src/libANGLE/Error.inl | 66 +- gfx/angle/src/libANGLE/Fence.cpp | 15 +- gfx/angle/src/libANGLE/Fence.h | 10 +- gfx/angle/src/libANGLE/Framebuffer.cpp | 310 ++-- gfx/angle/src/libANGLE/Framebuffer.h | 68 +- .../src/libANGLE/FramebufferAttachment.h | 19 +- gfx/angle/src/libANGLE/HandleAllocator.cpp | 3 +- gfx/angle/src/libANGLE/HandleAllocator.h | 2 +- gfx/angle/src/libANGLE/Program.cpp | 35 +- gfx/angle/src/libANGLE/Program.h | 14 +- gfx/angle/src/libANGLE/Query.cpp | 36 +- gfx/angle/src/libANGLE/Query.h | 15 +- gfx/angle/src/libANGLE/RefCountObject.cpp | 39 - gfx/angle/src/libANGLE/RefCountObject.h | 19 +- gfx/angle/src/libANGLE/Renderbuffer.cpp | 16 + gfx/angle/src/libANGLE/Renderbuffer.h | 13 +- gfx/angle/src/libANGLE/Sampler.cpp | 12 +- gfx/angle/src/libANGLE/Sampler.h | 8 +- gfx/angle/src/libANGLE/Shader.cpp | 12 +- gfx/angle/src/libANGLE/Shader.h | 10 +- gfx/angle/src/libANGLE/State.cpp | 271 ++- gfx/angle/src/libANGLE/State.h | 54 +- gfx/angle/src/libANGLE/Surface.cpp | 17 +- gfx/angle/src/libANGLE/Surface.h | 16 +- gfx/angle/src/libANGLE/Surface_unittest.cpp | 6 +- gfx/angle/src/libANGLE/Texture.cpp | 30 +- gfx/angle/src/libANGLE/Texture.h | 15 +- gfx/angle/src/libANGLE/TransformFeedback.cpp | 31 +- gfx/angle/src/libANGLE/TransformFeedback.h | 10 +- gfx/angle/src/libANGLE/VertexArray.cpp | 13 +- gfx/angle/src/libANGLE/VertexArray.h | 10 +- gfx/angle/src/libANGLE/angletypes.cpp | 9 + gfx/angle/src/libANGLE/angletypes.h | 17 +- gfx/angle/src/libANGLE/formatutils.cpp | 74 +- gfx/angle/src/libANGLE/moz.build | 8 +- gfx/angle/src/libANGLE/renderer/DeviceImpl.h | 1 + .../src/libANGLE/renderer/DisplayImpl.cpp | 5 +- gfx/angle/src/libANGLE/renderer/DisplayImpl.h | 10 +- .../src/libANGLE/renderer/FramebufferImpl.h | 33 +- .../libANGLE/renderer/FramebufferImpl_mock.h | 33 +- gfx/angle/src/libANGLE/renderer/ProgramImpl.h | 1 + .../src/libANGLE/renderer/ProgramImpl_mock.h | 11 + gfx/angle/src/libANGLE/renderer/QueryImpl.h | 6 +- gfx/angle/src/libANGLE/renderer/Renderer.h | 8 +- gfx/angle/src/libANGLE/renderer/SurfaceImpl.h | 2 +- .../src/libANGLE/renderer/d3d/BufferD3D.cpp | 15 +- .../src/libANGLE/renderer/d3d/BufferD3D.h | 1 + .../src/libANGLE/renderer/d3d/CompilerD3D.cpp | 24 +- .../src/libANGLE/renderer/d3d/CompilerD3D.h | 2 +- .../src/libANGLE/renderer/d3d/DeviceD3D.cpp | 64 +- .../src/libANGLE/renderer/d3d/DeviceD3D.h | 7 +- .../src/libANGLE/renderer/d3d/DisplayD3D.cpp | 71 +- .../src/libANGLE/renderer/d3d/DisplayD3D.h | 10 +- .../src/libANGLE/renderer/d3d/DynamicHLSL.cpp | 138 +- .../src/libANGLE/renderer/d3d/DynamicHLSL.h | 1 + .../libANGLE/renderer/d3d/FramebufferD3D.cpp | 128 +- .../libANGLE/renderer/d3d/FramebufferD3D.h | 47 +- .../src/libANGLE/renderer/d3d/ImageD3D.cpp | 18 +- .../src/libANGLE/renderer/d3d/ImageD3D.h | 12 +- .../renderer/d3d/IndexDataManager.cpp | 12 +- .../libANGLE/renderer/d3d/IndexDataManager.h | 19 +- .../src/libANGLE/renderer/d3d/ProgramD3D.cpp | 34 +- .../src/libANGLE/renderer/d3d/ProgramD3D.h | 10 +- .../src/libANGLE/renderer/d3d/RendererD3D.cpp | 156 +- .../src/libANGLE/renderer/d3d/RendererD3D.h | 67 +- .../src/libANGLE/renderer/d3d/SurfaceD3D.cpp | 27 +- .../src/libANGLE/renderer/d3d/SurfaceD3D.h | 26 +- .../src/libANGLE/renderer/d3d/TextureD3D.cpp | 34 +- .../libANGLE/renderer/d3d/d3d11/Blit11.cpp | 63 +- .../src/libANGLE/renderer/d3d/d3d11/Blit11.h | 15 +- .../libANGLE/renderer/d3d/d3d11/Buffer11.cpp | 104 +- .../libANGLE/renderer/d3d/d3d11/Buffer11.h | 8 +- .../libANGLE/renderer/d3d/d3d11/Clear11.cpp | 12 +- .../renderer/d3d/d3d11/Framebuffer11.cpp | 111 +- .../renderer/d3d/d3d11/Framebuffer11.h | 2 +- .../libANGLE/renderer/d3d/d3d11/Image11.cpp | 287 ++- .../src/libANGLE/renderer/d3d/d3d11/Image11.h | 14 +- .../renderer/d3d/d3d11/InputLayoutCache.cpp | 597 ++++--- .../renderer/d3d/d3d11/InputLayoutCache.h | 38 +- .../renderer/d3d/d3d11/NativeWindow.h | 23 +- .../renderer/d3d/d3d11/PixelTransfer11.cpp | 5 +- .../libANGLE/renderer/d3d/d3d11/Query11.cpp | 159 +- .../src/libANGLE/renderer/d3d/d3d11/Query11.h | 13 +- .../renderer/d3d/d3d11/RenderStateCache.cpp | 108 +- .../renderer/d3d/d3d11/RenderStateCache.h | 5 +- .../renderer/d3d/d3d11/Renderer11.cpp | 1577 ++++++++--------- .../libANGLE/renderer/d3d/d3d11/Renderer11.h | 149 +- .../renderer/d3d/d3d11/StateManager11.cpp | 937 +++++++++- .../renderer/d3d/d3d11/StateManager11.h | 142 +- .../renderer/d3d/d3d11/SwapChain11.cpp | 348 +++- .../libANGLE/renderer/d3d/d3d11/SwapChain11.h | 27 +- .../renderer/d3d/d3d11/TextureStorage11.cpp | 96 +- .../d3d/d3d11/gen_load_functions_table.py | 22 +- .../d3d/d3d11/gen_texture_format_table.py | 23 +- .../d3d/d3d11/load_functions_data.json | 9 + .../renderer/d3d/d3d11/load_functions_table.h | 5 +- .../d3d11/load_functions_table_autogen.cpp | 891 +++++----- .../renderer/d3d/d3d11/renderer11_utils.cpp | 359 +++- .../renderer/d3d/d3d11/renderer11_utils.h | 111 +- .../d3d/d3d11/texture_format_data.json | 345 +--- .../d3d/d3d11/texture_format_table.cpp | 414 ----- .../renderer/d3d/d3d11/texture_format_table.h | 16 +- .../d3d11/texture_format_table_autogen.cpp | 136 +- .../d3d/d3d11/texture_format_util.cpp | 286 --- .../renderer/d3d/d3d11/win32/NativeWindow.cpp | 141 +- .../d3d11/winrt/CoreWindowNativeWindow.cpp | 3 +- .../d3d11/winrt/InspectableNativeWindow.cpp | 12 +- .../winrt/SwapChainPanelNativeWindow.cpp | 3 +- .../renderer/d3d/d3d9/Framebuffer9.cpp | 14 +- .../libANGLE/renderer/d3d/d3d9/Framebuffer9.h | 2 +- .../src/libANGLE/renderer/d3d/d3d9/Image9.cpp | 36 +- .../src/libANGLE/renderer/d3d/d3d9/Image9.h | 11 +- .../src/libANGLE/renderer/d3d/d3d9/Query9.cpp | 36 +- .../src/libANGLE/renderer/d3d/d3d9/Query9.h | 11 +- .../libANGLE/renderer/d3d/d3d9/Renderer9.cpp | 501 ++---- .../libANGLE/renderer/d3d/d3d9/Renderer9.h | 92 +- .../renderer/d3d/d3d9/StateManager9.cpp | 903 ++++++++++ .../renderer/d3d/d3d9/StateManager9.h | 206 +++ .../libANGLE/renderer/d3d/d3d9/SwapChain9.cpp | 25 +- .../libANGLE/renderer/d3d/d3d9/SwapChain9.h | 10 +- .../renderer/d3d/d3d9/formatutils9.cpp | 25 +- .../renderer/d3d/d3d9/renderer9_utils.cpp | 2 + .../renderer/d3d/d3d9/vertexconversion.h | 5 +- .../libANGLE/renderer/d3d/loadimage_etc.cpp | 554 +++++- .../src/libANGLE/renderer/d3d/loadimage_etc.h | 10 + .../src/libANGLE/renderer/gl/DisplayGL.cpp | 12 +- .../src/libANGLE/renderer/gl/DisplayGL.h | 5 +- .../libANGLE/renderer/gl/FramebufferGL.cpp | 218 +-- .../src/libANGLE/renderer/gl/FramebufferGL.h | 33 +- .../src/libANGLE/renderer/gl/FunctionsGL.cpp | 492 ++++- .../src/libANGLE/renderer/gl/FunctionsGL.h | 11 + .../src/libANGLE/renderer/gl/ProgramGL.cpp | 14 +- .../src/libANGLE/renderer/gl/ProgramGL.h | 1 + .../src/libANGLE/renderer/gl/QueryGL.cpp | 72 +- gfx/angle/src/libANGLE/renderer/gl/QueryGL.h | 11 +- .../src/libANGLE/renderer/gl/RendererGL.cpp | 25 +- .../src/libANGLE/renderer/gl/RendererGL.h | 6 +- .../libANGLE/renderer/gl/StateManagerGL.cpp | 74 +- .../src/libANGLE/renderer/gl/StateManagerGL.h | 7 +- .../libANGLE/renderer/gl/VertexArrayGL.cpp | 10 +- .../src/libANGLE/renderer/gl/cgl/DisplayCGL.h | 5 + .../libANGLE/renderer/gl/cgl/DisplayCGL.mm | 24 +- .../renderer/gl/cgl/PbufferSurfaceCGL.h | 2 +- .../renderer/gl/cgl/PbufferSurfaceCGL.mm | 2 +- .../renderer/gl/cgl/WindowSurfaceCGL.h | 53 +- .../renderer/gl/cgl/WindowSurfaceCGL.mm | 405 ++--- .../libANGLE/renderer/gl/formatutilsgl.cpp | 38 +- .../libANGLE/renderer/gl/glx/DisplayGLX.cpp | 289 ++- .../src/libANGLE/renderer/gl/glx/DisplayGLX.h | 19 +- .../renderer/gl/glx/PbufferSurfaceGLX.cpp | 9 +- .../renderer/gl/glx/PbufferSurfaceGLX.h | 9 +- .../src/libANGLE/renderer/gl/glx/SurfaceGLX.h | 26 + .../renderer/gl/glx/WindowSurfaceGLX.cpp | 82 +- .../renderer/gl/glx/WindowSurfaceGLX.h | 8 +- .../libANGLE/renderer/gl/glx/platform_glx.h | 3 + .../libANGLE/renderer/gl/renderergl_utils.cpp | 39 +- .../libANGLE/renderer/gl/renderergl_utils.h | 3 + .../gl/wgl/DXGISwapChainWindowSurfaceWGL.cpp | 550 ++++++ .../gl/wgl/DXGISwapChainWindowSurfaceWGL.h | 104 ++ .../libANGLE/renderer/gl/wgl/DisplayWGL.cpp | 230 ++- .../src/libANGLE/renderer/gl/wgl/DisplayWGL.h | 23 + .../libANGLE/renderer/gl/wgl/FunctionsWGL.cpp | 20 +- .../libANGLE/renderer/gl/wgl/FunctionsWGL.h | 10 + .../renderer/gl/wgl/PbufferSurfaceWGL.cpp | 2 +- .../renderer/gl/wgl/PbufferSurfaceWGL.h | 2 +- .../renderer/gl/wgl/WindowSurfaceWGL.cpp | 7 +- .../renderer/gl/wgl/WindowSurfaceWGL.h | 5 +- gfx/angle/src/libANGLE/validationEGL.cpp | 156 +- gfx/angle/src/libANGLE/validationEGL.h | 13 +- gfx/angle/src/libANGLE/validationES.cpp | 742 ++++++-- gfx/angle/src/libANGLE/validationES.h | 122 +- gfx/angle/src/libANGLE/validationES2.cpp | 856 ++++++++- gfx/angle/src/libANGLE/validationES2.h | 101 +- gfx/angle/src/libANGLE/validationES3.cpp | 509 +++++- gfx/angle/src/libANGLE/validationES3.h | 201 ++- .../src/libANGLE/validationES_unittest.cpp | 35 +- gfx/angle/src/libEGL.gypi | 12 +- gfx/angle/src/libEGL/libEGL.cpp | 12 + gfx/angle/src/libEGL/libEGL.def | 2 + gfx/angle/src/libEGL/moz.build | 2 + gfx/angle/src/libGLESv2.gypi | 30 +- gfx/angle/src/libGLESv2/entry_points_egl.cpp | 120 +- .../src/libGLESv2/entry_points_egl_ext.cpp | 341 ++-- .../src/libGLESv2/entry_points_egl_ext.h | 6 + .../src/libGLESv2/entry_points_gles_2_0.cpp | 165 +- .../libGLESv2/entry_points_gles_2_0_ext.cpp | 720 +++++--- .../src/libGLESv2/entry_points_gles_2_0_ext.h | 49 + .../src/libGLESv2/entry_points_gles_3_0.cpp | 443 ++--- gfx/angle/src/libGLESv2/libGLESv2.cpp | 103 ++ gfx/angle/src/libGLESv2/libGLESv2.def | 15 + gfx/angle/src/libGLESv2/moz.build | 2 + gfx/angle/src/tests/BUILD.gn | 205 ++- gfx/angle/src/tests/angle_end2end_tests.gypi | 19 +- gfx/angle/src/tests/angle_perftests.gypi | 2 + gfx/angle/src/tests/angle_unittests.gypi | 2 + .../compiler_tests/CollectVariables_test.cpp | 154 +- .../EXT_blend_func_extended_test.cpp | 2 +- .../tests/compiler_tests/FragDepth_test.cpp | 4 +- .../GLSLCompatibilityOutput_test.cpp | 50 + .../compiler_tests/MalformedShader_test.cpp | 221 ++- .../tests/compiler_tests/ShCompile_test.cpp | 4 +- .../compiler_tests/ShaderExtension_test.cpp | 3 +- .../compiler_tests/ShaderVariable_test.cpp | 3 +- .../compiler_tests/TypeTracking_test.cpp | 6 +- .../compiler_tests/UnrollFlatten_test.cpp | 5 +- gfx/angle/src/tests/deqp.gypi | 425 ++--- .../tests/deqp_support/angle_deqp_gtest.cpp | 112 +- .../tests/deqp_support/dEQP-EGL-cases.txt.gz | Bin 14560 -> 0 bytes .../deqp_support/dEQP-GLES2-cases.txt.gz | Bin 79899 -> 0 bytes .../deqp_support/dEQP-GLES3-cases.txt.gz | Bin 179793 -> 0 bytes .../deqp_gles2_test_expectations.txt | 53 +- .../deqp_gles3_test_expectations.txt | 476 +---- .../tests/deqp_support/generate_case_lists.py | 45 - .../src/tests/deqp_tests/deqp_test_main.cpp | 63 - gfx/angle/src/tests/deqp_tests/deqp_tests.cpp | 160 -- gfx/angle/src/tests/deqp_tests/deqp_tests.h | 29 - gfx/angle/src/tests/deqp_tests/deqp_tests.txt | 40 - .../tests/deqp_tests/generate_deqp_tests.py | 44 - .../src/tests/egl_tests/EGLDeviceTest.cpp | 587 ++++++ .../egl_tests/EGLPresentPathD3D11Test.cpp | 378 ++++ .../src/tests/egl_tests/EGLSurfaceTest.cpp | 4 +- .../src/tests/egl_tests/EGLX11VisualTest.cpp | 213 +++ .../src/tests/gl_tests/BlendMinMaxTest.cpp | 32 +- .../gl_tests/BlitFramebufferANGLETest.cpp | 8 +- .../src/tests/gl_tests/BufferDataTest.cpp | 13 +- gfx/angle/src/tests/gl_tests/ClearTest.cpp | 227 ++- .../tests/gl_tests/CompressedTextureTest.cpp | 13 +- .../src/tests/gl_tests/CopyTexImageTest.cpp | 8 +- .../src/tests/gl_tests/CubeMapTextureTest.cpp | 8 +- .../src/tests/gl_tests/DebugMarkerTest.cpp | 2 +- gfx/angle/src/tests/gl_tests/DebugTest.cpp | 451 +++++ .../gl_tests/DepthStencilFormatsTest.cpp | 8 +- .../gl_tests/DiscardFramebufferEXTTest.cpp | 9 +- .../src/tests/gl_tests/DrawBuffersTest.cpp | 40 +- .../src/tests/gl_tests/DrawElementsTest.cpp | 2 +- .../src/tests/gl_tests/ETCTextureTest.cpp | 79 + .../src/tests/gl_tests/FenceSyncTests.cpp | 11 +- .../tests/gl_tests/FramebufferFormatsTest.cpp | 120 +- .../gl_tests/FramebufferRenderMipmapTest.cpp | 9 +- gfx/angle/src/tests/gl_tests/GLSLTest.cpp | 146 +- gfx/angle/src/tests/gl_tests/ImageTest.cpp | 9 +- .../tests/gl_tests/IncompleteTextureTest.cpp | 6 +- .../tests/gl_tests/IndexBufferOffsetTest.cpp | 5 +- .../src/tests/gl_tests/IndexedPointsTest.cpp | 18 +- .../src/tests/gl_tests/InstancingTest.cpp | 192 +- gfx/angle/src/tests/gl_tests/LineLoopTest.cpp | 22 +- .../src/tests/gl_tests/MaxTextureSizeTest.cpp | 3 + gfx/angle/src/tests/gl_tests/MipmapTest.cpp | 20 +- .../tests/gl_tests/ObjectAllocationTest.cpp | 54 + .../tests/gl_tests/OcclusionQueriesTest.cpp | 22 +- .../src/tests/gl_tests/PBOExtensionTest.cpp | 2 +- .../src/tests/gl_tests/PackUnpackTest.cpp | 12 +- gfx/angle/src/tests/gl_tests/PbufferTest.cpp | 8 +- .../src/tests/gl_tests/PointSpritesTest.cpp | 11 +- .../tests/gl_tests/ProvokingVertexTest.cpp | 11 +- .../tests/gl_tests/QueryDisplayAttribTest.cpp | 127 -- .../src/tests/gl_tests/ReadPixelsTest.cpp | 473 ++++- gfx/angle/src/tests/gl_tests/RendererTest.cpp | 87 +- .../src/tests/gl_tests/SRGBTextureTest.cpp | 7 +- .../tests/gl_tests/SimpleOperationTest.cpp | 12 +- .../tests/gl_tests/SixteenBppTextureTest.cpp | 7 +- .../src/tests/gl_tests/StateChangeTest.cpp | 244 +++ gfx/angle/src/tests/gl_tests/SwizzleTest.cpp | 2 +- gfx/angle/src/tests/gl_tests/TextureTest.cpp | 1016 +++++++++-- .../src/tests/gl_tests/TimerQueriesTest.cpp | 582 ++++++ .../tests/gl_tests/TransformFeedbackTest.cpp | 2 +- .../src/tests/gl_tests/UniformBufferTest.cpp | 10 +- gfx/angle/src/tests/gl_tests/UniformTest.cpp | 9 +- .../tests/gl_tests/UnpackAlignmentTest.cpp | 8 +- .../src/tests/gl_tests/UnpackRowLength.cpp | 4 +- .../tests/gl_tests/VertexAttributeTest.cpp | 9 +- gfx/angle/src/tests/gl_tests/ViewportTest.cpp | 8 +- .../src/tests/perf_tests/ANGLEPerfTest.cpp | 100 +- .../src/tests/perf_tests/ANGLEPerfTest.h | 39 +- .../src/tests/perf_tests/BufferSubData.cpp | 120 +- .../src/tests/perf_tests/DrawCallPerf.cpp | 16 +- .../tests/perf_tests/EGLInitializePerf.cpp | 17 +- .../tests/perf_tests/IndexConversionPerf.cpp | 23 +- .../tests/perf_tests/IndexDataManagerTest.cpp | 12 +- .../src/tests/perf_tests/InstancingPerf.cpp | 366 ++++ .../perf_tests/InterleavedAttributeData.cpp | 13 +- .../src/tests/perf_tests/PointSprites.cpp | 31 +- .../src/tests/perf_tests/TexSubImage.cpp | 13 +- .../src/tests/perf_tests/TextureSampling.cpp | 288 +++ gfx/angle/src/tests/test_utils/ANGLETest.cpp | 186 +- gfx/angle/src/tests/test_utils/ANGLETest.h | 68 +- .../tests/test_utils/angle_test_configs.cpp | 48 +- .../src/tests/test_utils/angle_test_configs.h | 5 + gfx/angle/src/tests/tests.gyp | 66 - .../gpu_test_expectations/HowToMakeChanges.md | 4 +- .../gpu_test_expectations/angle-mods.patch | 153 +- .../gpu_test_expectations/angle_config.h | 11 +- .../gpu_test_expectations/gpu_info.cc | 10 +- .../gpu_test_expectations/gpu_info.h | 5 + .../gpu_test_expectations/gpu_test_config.cc | 3 + .../gpu_test_expectations/gpu_test_config.h | 12 +- .../gpu_test_expectations_parser.cc | 19 +- gfx/layers/TransactionIdAllocator.h | 7 + gfx/layers/client/ClientLayerManager.cpp | 4 +- layout/base/nsPresContext.cpp | 22 +- layout/base/nsPresContext.h | 5 +- layout/base/nsRefreshDriver.cpp | 6 + layout/base/nsRefreshDriver.h | 3 +- layout/build/nsContentDLF.cpp | 3 +- netwerk/mime/nsMimeTypes.h | 1 + .../web-platform/meta/dom/historical.html.ini | 9 - .../web-platform/meta/dom/interfaces.html.ini | 9 - .../mediasniffer/nsMediaSniffer.cpp | 2 +- view/nsView.cpp | 6 +- view/nsView.h | 3 +- widget/nsIWidgetListener.cpp | 3 +- widget/nsIWidgetListener.h | 3 +- 463 files changed, 25382 insertions(+), 9912 deletions(-) create mode 100644 dom/media/DecoderDoctorDiagnostics.cpp create mode 100644 dom/media/DecoderDoctorDiagnostics.h rename dom/media/platforms/ffmpeg/{README.mcp => README.mozilla} (100%) create mode 100644 dom/media/platforms/omx/OmxPlatformLayer.cpp delete mode 100644 dom/tests/mochitest/dom-level2-core/test_localName03.html delete mode 100644 dom/tests/mochitest/dom-level2-core/test_prefix02.html create mode 100644 dom/webidl/DecoderDoctorNotification.webidl create mode 100644 gfx/angle/src/libANGLE/BinaryStream_unittest.cpp create mode 100644 gfx/angle/src/libANGLE/Debug.cpp create mode 100644 gfx/angle/src/libANGLE/Debug.h delete mode 100644 gfx/angle/src/libANGLE/RefCountObject.cpp delete mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.cpp delete mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_util.cpp create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d9/StateManager9.cpp create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d9/StateManager9.h create mode 100644 gfx/angle/src/libANGLE/renderer/gl/glx/SurfaceGLX.h create mode 100644 gfx/angle/src/libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.cpp create mode 100644 gfx/angle/src/libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.h create mode 100644 gfx/angle/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp delete mode 100644 gfx/angle/src/tests/deqp_support/dEQP-EGL-cases.txt.gz delete mode 100644 gfx/angle/src/tests/deqp_support/dEQP-GLES2-cases.txt.gz delete mode 100644 gfx/angle/src/tests/deqp_support/dEQP-GLES3-cases.txt.gz delete mode 100644 gfx/angle/src/tests/deqp_support/generate_case_lists.py delete mode 100644 gfx/angle/src/tests/deqp_tests/deqp_test_main.cpp delete mode 100644 gfx/angle/src/tests/deqp_tests/deqp_tests.cpp delete mode 100644 gfx/angle/src/tests/deqp_tests/deqp_tests.h delete mode 100644 gfx/angle/src/tests/deqp_tests/deqp_tests.txt delete mode 100644 gfx/angle/src/tests/deqp_tests/generate_deqp_tests.py create mode 100644 gfx/angle/src/tests/egl_tests/EGLDeviceTest.cpp create mode 100644 gfx/angle/src/tests/egl_tests/EGLPresentPathD3D11Test.cpp create mode 100644 gfx/angle/src/tests/egl_tests/EGLX11VisualTest.cpp create mode 100644 gfx/angle/src/tests/gl_tests/DebugTest.cpp create mode 100644 gfx/angle/src/tests/gl_tests/ETCTextureTest.cpp create mode 100644 gfx/angle/src/tests/gl_tests/ObjectAllocationTest.cpp delete mode 100644 gfx/angle/src/tests/gl_tests/QueryDisplayAttribTest.cpp create mode 100644 gfx/angle/src/tests/gl_tests/StateChangeTest.cpp create mode 100644 gfx/angle/src/tests/gl_tests/TimerQueriesTest.cpp create mode 100644 gfx/angle/src/tests/perf_tests/InstancingPerf.cpp create mode 100644 gfx/angle/src/tests/perf_tests/TextureSampling.cpp diff --git a/b2g/confvars.sh b/b2g/confvars.sh index 1dc8b45348..1638a21bdc 100755 --- a/b2g/confvars.sh +++ b/b2g/confvars.sh @@ -49,8 +49,6 @@ MOZ_USE_NATIVE_POPUP_WINDOWS=1 MOZ_XULRUNNER= -MOZ_MEDIA_NAVIGATOR=1 - MOZ_APP_ID={3c2e2abc-06d4-11e1-ac3b-374f68613e61} MOZ_TIME_MANAGER=1 diff --git a/browser/confvars.sh b/browser/confvars.sh index 692e0b97ec..162d47d8de 100644 --- a/browser/confvars.sh +++ b/browser/confvars.sh @@ -21,7 +21,6 @@ MOZ_ENABLE_SIGNMAR=1 MOZ_CHROME_FILE_FORMAT=omni MOZ_SERVICES_COMMON=1 -MOZ_MEDIA_NAVIGATOR=1 MOZ_SERVICES_CRYPTO=1 MOZ_SERVICES_SYNC=1 MOZ_APP_VERSION=`cat ${_topsrcdir}/$MOZ_BUILD_APP/config/version.txt` diff --git a/configure.in b/configure.in index 658bb58bd6..897c9d85cc 100644 --- a/configure.in +++ b/configure.in @@ -3711,7 +3711,6 @@ MOZ_WEBRTC_SIGNALING= MOZ_WEBRTC_ASSERT_ALWAYS=1 MOZ_WEBRTC_HARDWARE_AEC_NS= MOZ_ANDROID_OMX= -MOZ_MEDIA_NAVIGATOR= MOZ_OMX_PLUGIN= MOZ_WEBP=1 MOZ_NATIVE_LIBWEBP= @@ -5219,18 +5218,6 @@ if test -n "$MOZ_ANDROID_OMX"; then AC_DEFINE(MOZ_ANDROID_OMX) fi -dnl ======================================================== -dnl = Enable getUserMedia support -dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(media-navigator, -[ --enable-media-navigator Enable support for getUserMedia], - MOZ_MEDIA_NAVIGATOR=1, - MOZ_MEDIA_NAVIGATOR=) - -if test -n "$MOZ_MEDIA_NAVIGATOR"; then - AC_DEFINE(MOZ_MEDIA_NAVIGATOR) -fi - dnl ======================================================== dnl = Enable building OMX media plugin (B2G or Android) dnl ======================================================== diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index d72eeea0d5..d4e8341b21 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -79,10 +79,8 @@ #include "mozIApplication.h" #include "WidgetUtils.h" -#ifdef MOZ_MEDIA_NAVIGATOR #include "mozilla/dom/MediaDevices.h" #include "MediaManager.h" -#endif #ifdef MOZ_B2G_BT #include "BluetoothManager.h" #endif @@ -1347,7 +1345,6 @@ Navigator::SendBeacon(const nsAString& aUrl, return true; } -#ifdef MOZ_MEDIA_NAVIGATOR MediaDevices* Navigator::GetMediaDevices(ErrorResult& aRv) { @@ -1415,7 +1412,6 @@ Navigator::MozGetUserMediaDevices(const MediaStreamConstraints& aConstraints, aRv = manager->GetUserMediaDevices(mWindow, aConstraints, onsuccess, onerror, aInnerWindowID, aCallID); } -#endif DesktopNotificationCenter* Navigator::GetMozNotification(ErrorResult& aRv) @@ -2181,13 +2177,11 @@ Navigator::OnNavigation() return; } -#ifdef MOZ_MEDIA_NAVIGATOR // If MediaManager is open let it inform any live streams or pending callbacks MediaManager *manager = MediaManager::GetIfExists(); if (manager) { manager->OnNavigation(mWindow->WindowID()); } -#endif if (mCameraManager) { mCameraManager->OnNavigation(mWindow->WindowID()); } @@ -2290,7 +2284,6 @@ Navigator::HasNFCSupport(JSContext* /* unused */, JSObject* aGlobal) } #endif // MOZ_NFC -#ifdef MOZ_MEDIA_NAVIGATOR /* static */ bool Navigator::HasUserMediaSupport(JSContext* /* unused */, @@ -2300,7 +2293,6 @@ Navigator::HasUserMediaSupport(JSContext* /* unused */, return Preferences::GetBool("media.navigator.enabled", false) || Preferences::GetBool("media.peerconnection.enabled", false); } -#endif // MOZ_MEDIA_NAVIGATOR /* static */ bool diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index 3ed2f82dd4..ad88c90a7c 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -68,11 +68,9 @@ class MozIdleObserver; #ifdef MOZ_GAMEPAD class Gamepad; #endif // MOZ_GAMEPAD -#ifdef MOZ_MEDIA_NAVIGATOR class NavigatorUserMediaSuccessCallback; class NavigatorUserMediaErrorCallback; class MozGetUserMediaDevicesSuccessCallback; -#endif // MOZ_MEDIA_NAVIGATOR namespace network { class Connection; @@ -291,7 +289,6 @@ public: const Nullable& aData, ErrorResult& aRv); -#ifdef MOZ_MEDIA_NAVIGATOR void MozGetUserMedia(const MediaStreamConstraints& aConstraints, NavigatorUserMediaSuccessCallback& aOnSuccess, NavigatorUserMediaErrorCallback& aOnError, @@ -302,7 +299,6 @@ public: uint64_t aInnerWindowID, const nsAString& aCallID, ErrorResult& aRv); -#endif // MOZ_MEDIA_NAVIGATOR already_AddRefed ServiceWorker(); @@ -327,10 +323,8 @@ public: #ifdef MOZ_NFC static bool HasNFCSupport(JSContext* /* unused */, JSObject* aGlobal); #endif // MOZ_NFC -#ifdef MOZ_MEDIA_NAVIGATOR static bool HasUserMediaSupport(JSContext* /* unused */, JSObject* /* unused */); -#endif // MOZ_MEDIA_NAVIGATOR static bool HasDataStoreSupport(nsIPrincipal* aPrincipal); diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 262c5570a6..e6ed9b6ef0 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -2420,6 +2420,19 @@ nsDOMWindowUtils::AdvanceTimeAndRefresh(int64_t aMilliseconds) return NS_OK; } +NS_IMETHODIMP +nsDOMWindowUtils::GetLastTransactionId(uint64_t *aLastTransactionId) +{ + nsPresContext* presContext = GetPresContext(); + if (!presContext) { + return NS_ERROR_UNEXPECTED; + } + + nsRefreshDriver* driver = presContext->GetRootPresContext()->RefreshDriver(); + *aLastTransactionId = driver->LastTransactionId(); + return NS_OK; +} + NS_IMETHODIMP nsDOMWindowUtils::RestoreNormalRefresh() { diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 4767e9dc7d..f58979ca13 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -253,9 +253,7 @@ #include "nsISpeculativeConnect.h" -#ifdef MOZ_MEDIA_NAVIGATOR #include "mozilla/MediaManager.h" -#endif // MOZ_MEDIA_NAVIGATOR #ifdef MOZ_WEBRTC #include "IPeerConnection.h" #endif // MOZ_WEBRTC @@ -8866,13 +8864,11 @@ nsDocument::CanSavePresentation(nsIRequest *aNewRequest) } } -#ifdef MOZ_MEDIA_NAVIGATOR // Check if we have active GetUserMedia use if (MediaManager::Exists() && win && MediaManager::Get()->IsWindowStillActive(win->WindowID())) { return false; } -#endif // MOZ_MEDIA_NAVIGATOR #ifdef MOZ_WEBRTC // Check if we have active PeerConnections diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h index 9704e1921d..bb8e0fcb9e 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -917,6 +917,11 @@ public: */ nsINode* SubtreeRoot() const; + nsINode* RootNode() const + { + return SubtreeRoot(); + } + /** * See nsIDOMEventTarget */ diff --git a/dom/base/test/test_bug352728.html b/dom/base/test/test_bug352728.html index be1ec01e7c..faf2f681a8 100644 --- a/dom/base/test/test_bug352728.html +++ b/dom/base/test/test_bug352728.html @@ -40,8 +40,8 @@ function testCharacterData(aNode, aText) is(aNode.length, aText.length, "Text length should match"); is(aNode.data, aText, "Text content should match"); is(aNode.nodeValue, aText, "Check nodeValue"); - is(aNode.localName, null, "Check localName") - is(aNode.namespaceURI, null, "Check namespaceURI"); + is(aNode.localName, undefined, "Check localName") + is(aNode.namespaceURI, undefined, "Check namespaceURI"); } function testComment(aText) @@ -90,8 +90,8 @@ function testPI(aTarget, aData, aShouldSucceed, aReason) is(pi.data, aData, "Check data"); is(pi.nodeName, aTarget, "Check nodeName"); is(pi.nodeValue, aData, "Check nodeValue"); - is(pi.localName, null, "Check localName") - is(pi.namespaceURI, null, "Check namespaceURI"); + is(pi.localName, undefined, "Check localName") + is(pi.namespaceURI, undefined, "Check namespaceURI"); is(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE, "Check nodeType"); diff --git a/dom/base/test/test_bug352728.xhtml b/dom/base/test/test_bug352728.xhtml index 93c7938e49..6f85810d61 100644 --- a/dom/base/test/test_bug352728.xhtml +++ b/dom/base/test/test_bug352728.xhtml @@ -64,8 +64,8 @@ function testCharacterData(aNode, aText) is(aNode.length, aText.length, "Text length should match"); is(aNode.data, aText, "Text content should match"); is(aNode.nodeValue, aText, "Check nodeValue"); - is(aNode.localName, null, "Check localName") - is(aNode.namespaceURI, null, "Check namespaceURI"); + is(aNode.localName, undefined, "Check localName") + is(aNode.namespaceURI, undefined, "Check namespaceURI"); } function testComment(aText) @@ -134,8 +134,8 @@ function testPI(aTarget, aData, aShouldSucceed, aReason) is(pi.data, aData, "Check data"); is(pi.nodeName, aTarget, "Check nodeName"); is(pi.nodeValue, aData, "Check nodeValue"); - is(pi.localName, null, "Check localName") - is(pi.namespaceURI, null, "Check namespaceURI"); + is(pi.localName, undefined, "Check localName") + is(pi.namespaceURI, undefined, "Check namespaceURI"); is(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE, "Check nodeType"); diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 73b09be38f..b922c7c0fd 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -4144,17 +4144,17 @@ class IDLAttribute(IDLInterfaceMember): raise WebIDLError("[Unscopable] is only allowed on non-static " "attributes and operations", [attr.location, self.location]) - elif (identifier == "Throws" or - identifier == "NewObject" or - identifier == "ChromeOnly" or - identifier == "UnsafeInPrerendering" or - identifier == "Pref" or + elif (identifier == "Pref" or identifier == "Deprecated" or identifier == "SetterThrows" or + identifier == "Throws" or identifier == "GetterThrows" or + identifier == "ChromeOnly" or identifier == "Func" or identifier == "Frozen" or identifier == "AvailableIn" or + identifier == "NewObject" or + identifier == "UnsafeInPrerendering" or identifier == "CheckAnyPermissions" or identifier == "CheckAllPermissions" or identifier == "BinaryName"): diff --git a/dom/canvas/WebGLShaderValidator.cpp b/dom/canvas/WebGLShaderValidator.cpp index a10cf8d645..1bfe5161f2 100644 --- a/dom/canvas/WebGLShaderValidator.cpp +++ b/dom/canvas/WebGLShaderValidator.cpp @@ -134,7 +134,7 @@ WebGLContext::CreateShaderValidator(GLenum shaderType) const ShShaderSpec spec = IsWebGL2() ? SH_WEBGL2_SPEC : SH_WEBGL_SPEC; ShShaderOutput outputLanguage = gl->IsGLES() ? SH_ESSL_OUTPUT - : SH_GLSL_OUTPUT; + : SH_GLSL_COMPATIBILITY_OUTPUT; // If we're using WebGL2 we want a more specific version of GLSL if (IsWebGL2()) diff --git a/dom/canvas/test/webgl-mochitest.ini b/dom/canvas/test/webgl-mochitest.ini index dcd907761e..919c5a1125 100644 --- a/dom/canvas/test/webgl-mochitest.ini +++ b/dom/canvas/test/webgl-mochitest.ini @@ -15,7 +15,7 @@ fail-if = (os == 'android') [webgl-mochitest/ensure-exts/test_EXT_color_buffer_half_float.html] fail-if = (os == 'android') [webgl-mochitest/ensure-exts/test_EXT_disjoint_timer_query.html] -fail-if = (os == 'android') || (os == 'mac') || (os == 'win') +fail-if = (os == 'android') || (os == 'mac') || (os == 'win' && (os_version == '5.1' || os_version == '6.1')) [webgl-mochitest/ensure-exts/test_EXT_frag_depth.html] fail-if = (os == 'android') [webgl-mochitest/ensure-exts/test_EXT_sRGB.html] diff --git a/dom/events/NotifyPaintEvent.cpp b/dom/events/NotifyPaintEvent.cpp index b507da5a37..35ff2af808 100644 --- a/dom/events/NotifyPaintEvent.cpp +++ b/dom/events/NotifyPaintEvent.cpp @@ -19,7 +19,8 @@ NotifyPaintEvent::NotifyPaintEvent(EventTarget* aOwner, nsPresContext* aPresContext, WidgetEvent* aEvent, EventMessage aEventMessage, - nsInvalidateRequestList* aInvalidateRequests) + nsInvalidateRequestList* aInvalidateRequests, + uint64_t aTransactionId) : Event(aOwner, aPresContext, aEvent) { if (mEvent) { @@ -28,6 +29,8 @@ NotifyPaintEvent::NotifyPaintEvent(EventTarget* aOwner, if (aInvalidateRequests) { mInvalidateRequests.AppendElements(Move(aInvalidateRequests->mRequests)); } + + mTransactionId = aTransactionId; } NS_INTERFACE_MAP_BEGIN(NotifyPaintEvent) @@ -154,6 +157,19 @@ NotifyPaintEvent::Deserialize(const IPC::Message* aMsg, void** aIter) return true; } +NS_IMETHODIMP +NotifyPaintEvent::GetTransactionId(uint64_t* aTransactionId) +{ + *aTransactionId = mTransactionId; + return NS_OK; +} + +uint64_t +NotifyPaintEvent::TransactionId() +{ + return mTransactionId; +} + } // namespace dom } // namespace mozilla @@ -165,10 +181,11 @@ NS_NewDOMNotifyPaintEvent(EventTarget* aOwner, nsPresContext* aPresContext, WidgetEvent* aEvent, EventMessage aEventMessage, - nsInvalidateRequestList* aInvalidateRequests) + nsInvalidateRequestList* aInvalidateRequests, + uint64_t aTransactionId) { RefPtr it = new NotifyPaintEvent(aOwner, aPresContext, aEvent, aEventMessage, - aInvalidateRequests); + aInvalidateRequests, aTransactionId); return it.forget(); } diff --git a/dom/events/NotifyPaintEvent.h b/dom/events/NotifyPaintEvent.h index 5b12b8b211..001795ed1e 100644 --- a/dom/events/NotifyPaintEvent.h +++ b/dom/events/NotifyPaintEvent.h @@ -29,7 +29,8 @@ public: nsPresContext* aPresContext, WidgetEvent* aEvent, EventMessage aEventMessage, - nsInvalidateRequestList* aInvalidateRequests); + nsInvalidateRequestList* aInvalidateRequests, + uint64_t aTransactionId); NS_DECL_ISUPPORTS_INHERITED @@ -55,6 +56,8 @@ public: already_AddRefed PaintRequests(); + uint64_t TransactionId(); + protected: ~NotifyPaintEvent() {} @@ -62,6 +65,7 @@ private: nsRegion GetRegion(); nsTArray mInvalidateRequests; + uint64_t mTransactionId; }; } // namespace dom @@ -75,6 +79,7 @@ NS_NewDOMNotifyPaintEvent(mozilla::dom::EventTarget* aOwner, mozilla::EventMessage aEventMessage = mozilla::eVoidEvent, nsInvalidateRequestList* aInvalidateRequests = - nullptr); + nullptr, + uint64_t aTransactionId = 0); #endif // mozilla_dom_NotifyPaintEvent_h_ diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 97306735d8..56481ac4db 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -68,6 +68,7 @@ #include "mozilla/dom/MediaSource.h" #include "MediaMetadataManager.h" #include "MediaSourceDecoder.h" +#include "DOMMediaStream.h" #include "AudioStreamTrack.h" #include "VideoStreamTrack.h" #include "MediaTrackList.h" @@ -84,6 +85,7 @@ #include "mozilla/dom/TextTrack.h" #include "nsIContentPolicy.h" #include "mozilla/Telemetry.h" +#include "DecoderDoctorDiagnostics.h" #include "ImageContainer.h" #include "nsRange.h" @@ -1045,12 +1047,19 @@ void HTMLMediaElement::LoadFromSourceChildren() // If we have a type attribute, it must be a supported type. nsAutoString type; - if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) && - GetCanPlay(type) == CANPLAY_NO) { - DispatchAsyncSourceError(child); - const char16_t* params[] = { type.get(), src.get() }; - ReportLoadError("MediaLoadUnsupportedTypeAttribute", params, ArrayLength(params)); - continue; + if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type)) { + DecoderDoctorDiagnostics diagnostics; + CanPlayStatus canPlay = GetCanPlay(type, &diagnostics); + if (canPlay != CANPLAY_NO) { + diagnostics.SetCanPlay(); + } + diagnostics.StoreDiagnostics(OwnerDoc(), type, __func__); + if (canPlay == CANPLAY_NO) { + DispatchAsyncSourceError(child); + const char16_t* params[] = { type.get(), src.get() }; + ReportLoadError("MediaLoadUnsupportedTypeAttribute", params, ArrayLength(params)); + continue; + } } nsAutoString media; HTMLSourceElement *childSrc = HTMLSourceElement::FromContent(child); @@ -2848,7 +2857,8 @@ void HTMLMediaElement::UnbindFromTree(bool aDeep, /* static */ CanPlayStatus -HTMLMediaElement::GetCanPlay(const nsAString& aType) +HTMLMediaElement::GetCanPlay(const nsAString& aType, + DecoderDoctorDiagnostics* aDiagnostics) { nsContentTypeParser parser(aType); nsAutoString mimeType; @@ -2862,13 +2872,20 @@ HTMLMediaElement::GetCanPlay(const nsAString& aType) NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType); return DecoderTraits::CanHandleMediaType(mimeTypeUTF8.get(), NS_SUCCEEDED(rv), - codecs); + codecs, + aDiagnostics); } NS_IMETHODIMP HTMLMediaElement::CanPlayType(const nsAString& aType, nsAString& aResult) { - switch (GetCanPlay(aType)) { + DecoderDoctorDiagnostics diagnostics; + CanPlayStatus canPlay = GetCanPlay(aType, &diagnostics); + if (canPlay != CANPLAY_NO) { + diagnostics.SetCanPlay(); + } + diagnostics.StoreDiagnostics(OwnerDoc(), aType, __func__); + switch (canPlay) { case CANPLAY_NO: aResult.Truncate(); break; @@ -2928,7 +2945,15 @@ nsresult HTMLMediaElement::InitializeDecoderForChannel(nsIChannel* aChannel, aChannel->GetContentType(mimeType); NS_ASSERTION(!mimeType.IsEmpty(), "We should have the Content-Type."); - RefPtr decoder = DecoderTraits::CreateDecoder(mimeType, this); + DecoderDoctorDiagnostics diagnostics; + RefPtr decoder = + DecoderTraits::CreateDecoder(mimeType, this, &diagnostics); + if (decoder) { + diagnostics.SetCanPlay(); + } + diagnostics.StoreDiagnostics(OwnerDoc(), + NS_ConvertASCIItoUTF16(mimeType), + __func__); if (!decoder) { nsAutoString src; GetCurrentSrc(src); diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index 9b60447a10..5f69aa95b9 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -12,7 +12,6 @@ #include "nsCycleCollectionParticipant.h" #include "nsIObserver.h" #include "mozilla/CORSMode.h" -#include "DOMMediaStream.h" #include "DecoderTraits.h" #include "nsIAudioChannelAgent.h" #include "mozilla/Attributes.h" @@ -38,6 +37,8 @@ typedef uint16_t nsMediaNetworkState; typedef uint16_t nsMediaReadyState; namespace mozilla { +class DecoderDoctorDiagnostics; +class DOMMediaStream; class ErrorResult; class MediaResource; class MediaDecoder; @@ -322,7 +323,8 @@ public: // Returns the CanPlayStatus indicating if we can handle the // full MIME type including the optional codecs parameter. - static CanPlayStatus GetCanPlay(const nsAString& aType); + static CanPlayStatus GetCanPlay(const nsAString& aType, + DecoderDoctorDiagnostics* aDiagnostics); /** * Called when a child source element is added to this media element. This diff --git a/dom/imptests/failures/html/dom/test_interfaces.html.json b/dom/imptests/failures/html/dom/test_interfaces.html.json index 830faf148b..83a6faac59 100644 --- a/dom/imptests/failures/html/dom/test_interfaces.html.json +++ b/dom/imptests/failures/html/dom/test_interfaces.html.json @@ -32,9 +32,6 @@ "DocumentType interface: document.doctype must inherit property \"replace\" with the proper type (5)": true, "DocumentType interface: calling replace([object Object],[object Object]) on document.doctype with too few arguments must throw TypeError": true, "Element interface: existence and properties of interface object": true, - "Element interface: attribute namespaceURI": true, - "Element interface: attribute prefix": true, - "Element interface: attribute localName": true, "Element interface: operation prepend([object Object],[object Object])": true, "Element interface: operation append([object Object],[object Object])": true, "Element interface: operation before([object Object],[object Object])": true, diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index d86c748cbc..f8d5609283 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -106,6 +106,11 @@ interface nsIDOMWindowUtils : nsISupports { */ void updateLayerTree(); + /** + * Get the last used layer transaction id for this window's refresh driver. + */ + readonly attribute unsigned long long lastTransactionId; + /** * Information retrieved from the tag. * See nsContentUtils::GetViewportInfo for more information. diff --git a/dom/interfaces/events/nsIDOMNotifyPaintEvent.idl b/dom/interfaces/events/nsIDOMNotifyPaintEvent.idl index 905876df59..6df07a1c86 100644 --- a/dom/interfaces/events/nsIDOMNotifyPaintEvent.idl +++ b/dom/interfaces/events/nsIDOMNotifyPaintEvent.idl @@ -35,5 +35,7 @@ interface nsIDOMNotifyPaintEvent : nsISupports readonly attribute nsIDOMClientRect boundingClientRect; readonly attribute nsISupports /* PaintRequestList */ paintRequests; + + readonly attribute unsigned long long transactionId; }; diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index d2b6274240..335e11cc2c 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -109,6 +109,10 @@ MediaLoadSourceMediaNotMatched=Specified "media" attribute of "%1$S" does not ma MediaLoadUnsupportedMimeType=HTTP "Content-Type" of "%1$S" is not supported. Load of media resource %2$S failed. # LOCALIZATION NOTE: %S is the URL of the media resource which failed to load because of error in decoding. MediaLoadDecodeError=Media resource %S could not be decoded. +# LOCALIZATION NOTE: %S is a comma-separated list of codecs (e.g. 'video/mp4, video/webm') +MediaCannotPlayNoDecoders=Cannot play media. No decoders for requested formats: %S +# LOCALIZATION NOTE: %S is a comma-separated list of codecs (e.g. 'video/mp4, video/webm') +MediaNoDecoders=No decoders for some of the requested formats: %S # LOCALIZATION NOTE: Do not translate "MediaRecorder". MediaRecorderMultiTracksNotSupported=MediaRecorder does not support recording multiple tracks of the same type at this time. # LOCALIZATION NOTE: %S is the ID of the MediaStreamTrack passed to MediaStream.addTrack(). Do not translate "MediaStreamTrack" and "AudioChannel". diff --git a/dom/media/ADTSDecoder.cpp b/dom/media/ADTSDecoder.cpp index 5030326d7d..08cdfe9765 100644 --- a/dom/media/ADTSDecoder.cpp +++ b/dom/media/ADTSDecoder.cpp @@ -34,7 +34,8 @@ ADTSDecoder::IsEnabled() { PDMFactory::Init(); RefPtr platform = new PDMFactory(); - return (platform && platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm"))); + return (platform && platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm"), + /* DecoderDoctorDiagnostics* */ nullptr)); } /* static */ bool diff --git a/dom/media/DecoderDoctorDiagnostics.cpp b/dom/media/DecoderDoctorDiagnostics.cpp new file mode 100644 index 0000000000..c2851191cb --- /dev/null +++ b/dom/media/DecoderDoctorDiagnostics.cpp @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DecoderDoctorDiagnostics.h" + +#include "mozilla/Logging.h" + +static mozilla::LazyLogModule sDecoderDoctorLog("DecoderDoctor"); +#define DD_LOG(level, arg, ...) MOZ_LOG(sDecoderDoctorLog, level, (arg, ##__VA_ARGS__)) +#define DD_DEBUG(arg, ...) DD_LOG(mozilla::LogLevel::Debug, arg, ##__VA_ARGS__) +#define DD_WARN(arg, ...) DD_LOG(mozilla::LogLevel::Warning, arg, ##__VA_ARGS__) + +namespace mozilla +{ + +void +DecoderDoctorDiagnostics::StoreDiagnostics(nsIDocument* aDocument, + const nsAString& aFormat, + const char* aCallSite) +{ + if (NS_WARN_IF(!aDocument)) { + DD_WARN("DecoderDoctorDiagnostics[%p]::StoreDiagnostics(nsIDocument* aDocument=nullptr, format='%s', call site '%s')", + this, NS_ConvertUTF16toUTF8(aFormat).get(), aCallSite); + return; + } + + // TODO: Actually analyze data. + DD_DEBUG("DecoderDoctorDiagnostics[%p]::StoreDiagnostics(nsIDocument* aDocument=%p, format='%s', call site '%s')", + this, aDocument, NS_ConvertUTF16toUTF8(aFormat).get(), aCallSite); +} + +} // namespace mozilla diff --git a/dom/media/DecoderDoctorDiagnostics.h b/dom/media/DecoderDoctorDiagnostics.h new file mode 100644 index 0000000000..cffb8d01dd --- /dev/null +++ b/dom/media/DecoderDoctorDiagnostics.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DecoderDoctorDiagnostics_h_ +#define DecoderDoctorDiagnostics_h_ + +class nsIDocument; +class nsAString; + +namespace mozilla { + +// DecoderDoctorDiagnostics class, used to gather data from PDMs/DecoderTraits, +// and then notify the user about issues preventing (or worsening) playback. +// +// The expected usage is: +// 1. Instantiate a DecoderDoctorDiagnostics in a function (close to the point +// where a webpage is trying to know whether some MIME types can be played, +// or trying to play a media file). +// 2. Pass a pointer to the DecoderDoctorDiagnostics structure to one of the +// CanPlayStatus/IsTypeSupported/(others?). During that call, some PDMs may +// add relevant diagnostic information. +// 3. Analyze the collected diagnostics, and optionally dispatch an event to the +// UX, to notify the user about potential playback issues and how to resolve +// them. +// +// This class' methods must be called from the main thread. + +class DecoderDoctorDiagnostics +{ +public: + // Store the diagnostic information collected so far on a document for a + // given format. All diagnostics for a document will be analyzed together + // within a short timeframe. + // Should only be called once. + void StoreDiagnostics(nsIDocument* aDocument, + const nsAString& aFormat, + const char* aCallSite); + + // Methods to record diagnostic information: + + void SetCanPlay() { mCanPlay = true; } + bool CanPlay() const { return mCanPlay; } + +private: + // True if there is at least one decoder that can play the media. + bool mCanPlay = false; +}; + +} // namespace mozilla + +#endif diff --git a/dom/media/DecoderTraits.cpp b/dom/media/DecoderTraits.cpp index 92de066a44..38ee59e7ed 100644 --- a/dom/media/DecoderTraits.cpp +++ b/dom/media/DecoderTraits.cpp @@ -59,6 +59,8 @@ #include "ADTSDecoder.h" #include "ADTSDemuxer.h" +#include "nsPluginHost.h" + namespace mozilla { @@ -284,7 +286,7 @@ bool DecoderTraits::DecoderWaitsForOnConnected(const nsACString& aMimeType) { static bool IsAndroidMediaType(const nsACString& aType) { - if (!MediaDecoder::IsAndroidMediaEnabled()) { + if (!MediaDecoder::IsAndroidMediaPluginEnabled()) { return false; } @@ -306,19 +308,22 @@ IsDirectShowSupportedType(const nsACString& aType) #ifdef MOZ_FMP4 static bool IsMP4SupportedType(const nsACString& aType, + DecoderDoctorDiagnostics* aDiagnostics, const nsAString& aCodecs = EmptyString()) { - return MP4Decoder::CanHandleMediaType(aType, aCodecs); + return MP4Decoder::CanHandleMediaType(aType, aCodecs, aDiagnostics); } #endif /* static */ bool -DecoderTraits::IsMP4TypeAndEnabled(const nsACString& aType) +DecoderTraits::IsMP4TypeAndEnabled(const nsACString& aType, + DecoderDoctorDiagnostics* aDiagnostics) { #ifdef MOZ_FMP4 - return IsMP4SupportedType(aType); -#endif + return IsMP4SupportedType(aType, aDiagnostics); +#else return false; +#endif } static bool @@ -327,8 +332,9 @@ IsMP3SupportedType(const nsACString& aType, { #ifdef MOZ_OMX_DECODER return false; -#endif +#else return MP3Decoder::CanHandleMediaType(aType, aCodecs); +#endif } static bool @@ -346,7 +352,8 @@ IsWAVSupportedType(const nsACString& aType, } /* static */ -bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType) +bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType, + DecoderDoctorDiagnostics* aDiagnostics) { if (IsWaveType(nsDependentCString(aMIMEType))) { // We should not return true for Wave types, since there are some @@ -356,13 +363,27 @@ bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType) // means. return false; } - return CanHandleMediaType(aMIMEType, false, EmptyString()) != CANPLAY_NO; + + // If an external plugin which can handle quicktime video is available + // (and not disabled), prefer it over native playback as there several + // codecs found in the wild that we do not handle. + if (nsDependentCString(aMIMEType).EqualsASCII("video/quicktime")) { + RefPtr pluginHost = nsPluginHost::GetInst(); + if (pluginHost && + pluginHost->HavePluginForType(nsDependentCString(aMIMEType))) { + return false; + } + } + + return CanHandleMediaType(aMIMEType, false, EmptyString(), aDiagnostics) + != CANPLAY_NO; } /* static */ CanPlayStatus DecoderTraits::CanHandleCodecsType(const char* aMIMEType, - const nsAString& aRequestedCodecs) + const nsAString& aRequestedCodecs, + DecoderDoctorDiagnostics* aDiagnostics) { char const* const* codecList = nullptr; #ifdef MOZ_RAW @@ -388,8 +409,8 @@ DecoderTraits::CanHandleCodecsType(const char* aMIMEType, } #endif #ifdef MOZ_FMP4 - if (IsMP4TypeAndEnabled(nsDependentCString(aMIMEType))) { - if (IsMP4SupportedType(nsDependentCString(aMIMEType), aRequestedCodecs)) { + if (IsMP4TypeAndEnabled(nsDependentCString(aMIMEType), aDiagnostics)) { + if (IsMP4SupportedType(nsDependentCString(aMIMEType), aDiagnostics, aRequestedCodecs)) { return CANPLAY_YES; } else { // We can only reach this position if a particular codec was requested, @@ -422,7 +443,7 @@ DecoderTraits::CanHandleCodecsType(const char* aMIMEType, DirectShowDecoder::GetSupportedCodecs(nsDependentCString(aMIMEType), &codecList); #endif #ifdef MOZ_ANDROID_OMX - if (MediaDecoder::IsAndroidMediaEnabled()) { + if (MediaDecoder::IsAndroidMediaPluginEnabled()) { EnsureAndroidMediaPluginHost()->FindDecoder(nsDependentCString(aMIMEType), &codecList); } #endif @@ -455,12 +476,15 @@ DecoderTraits::CanHandleCodecsType(const char* aMIMEType, CanPlayStatus DecoderTraits::CanHandleMediaType(const char* aMIMEType, bool aHaveRequestedCodecs, - const nsAString& aRequestedCodecs) + const nsAString& aRequestedCodecs, + DecoderDoctorDiagnostics* aDiagnostics) { MOZ_ASSERT(NS_IsMainThread()); if (aHaveRequestedCodecs) { - CanPlayStatus result = CanHandleCodecsType(aMIMEType, aRequestedCodecs); + CanPlayStatus result = CanHandleCodecsType(aMIMEType, + aRequestedCodecs, + aDiagnostics); if (result == CANPLAY_NO || result == CANPLAY_YES) { return result; } @@ -476,7 +500,7 @@ DecoderTraits::CanHandleMediaType(const char* aMIMEType, if (IsWaveType(nsDependentCString(aMIMEType))) { return CANPLAY_MAYBE; } - if (IsMP4TypeAndEnabled(nsDependentCString(aMIMEType))) { + if (IsMP4TypeAndEnabled(nsDependentCString(aMIMEType), aDiagnostics)) { return CANPLAY_MAYBE; } #if !defined(MOZ_OMX_WEBM_DECODER) @@ -501,7 +525,7 @@ DecoderTraits::CanHandleMediaType(const char* aMIMEType, } #endif #ifdef MOZ_ANDROID_OMX - if (MediaDecoder::IsAndroidMediaEnabled() && + if (MediaDecoder::IsAndroidMediaPluginEnabled() && EnsureAndroidMediaPluginHost()->FindDecoder(nsDependentCString(aMIMEType), nullptr)) { return CANPLAY_MAYBE; } @@ -517,13 +541,15 @@ DecoderTraits::CanHandleMediaType(const char* aMIMEType, // Instantiates but does not initialize decoder. static already_AddRefed -InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner) +InstantiateDecoder(const nsACString& aType, + MediaDecoderOwner* aOwner, + DecoderDoctorDiagnostics* aDiagnostics) { MOZ_ASSERT(NS_IsMainThread()); RefPtr decoder; #ifdef MOZ_FMP4 - if (IsMP4SupportedType(aType)) { + if (IsMP4SupportedType(aType, aDiagnostics)) { decoder = new MP4Decoder(aOwner); return decoder.forget(); } @@ -578,7 +604,7 @@ InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner) } #endif #ifdef MOZ_ANDROID_OMX - if (MediaDecoder::IsAndroidMediaEnabled() && + if (MediaDecoder::IsAndroidMediaPluginEnabled() && EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) { decoder = new AndroidMediaDecoder(aOwner, aType); return decoder.forget(); @@ -604,10 +630,12 @@ InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner) /* static */ already_AddRefed -DecoderTraits::CreateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner) +DecoderTraits::CreateDecoder(const nsACString& aType, + MediaDecoderOwner* aOwner, + DecoderDoctorDiagnostics* aDiagnostics) { MOZ_ASSERT(NS_IsMainThread()); - return InstantiateDecoder(aType, aOwner); + return InstantiateDecoder(aType, aOwner, aDiagnostics); } /* static */ @@ -620,7 +648,7 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac return decoderReader; } #ifdef MOZ_FMP4 - if (IsMP4SupportedType(aType)) { + if (IsMP4SupportedType(aType, /* DecoderDoctorDiagnostics* */ nullptr)) { decoderReader = new MediaFormatReader(aDecoder, new MP4Demuxer(aDecoder->GetResource())); } else #endif @@ -650,7 +678,7 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac } else #endif #ifdef MOZ_ANDROID_OMX - if (MediaDecoder::IsAndroidMediaEnabled() && + if (MediaDecoder::IsAndroidMediaPluginEnabled() && EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) { decoderReader = new AndroidMediaReader(aDecoder, aType); } else @@ -691,10 +719,10 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType) #endif IsWebMSupportedType(aType) || #ifdef MOZ_ANDROID_OMX - (MediaDecoder::IsAndroidMediaEnabled() && IsAndroidMediaType(aType)) || + (MediaDecoder::IsAndroidMediaPluginEnabled() && IsAndroidMediaType(aType)) || #endif #ifdef MOZ_FMP4 - IsMP4SupportedType(aType) || + IsMP4SupportedType(aType, /* DecoderDoctorDiagnostics* */ nullptr) || #endif IsMP3SupportedType(aType) || IsAACSupportedType(aType) || diff --git a/dom/media/DecoderTraits.h b/dom/media/DecoderTraits.h index e945414207..82218212c8 100644 --- a/dom/media/DecoderTraits.h +++ b/dom/media/DecoderTraits.h @@ -15,6 +15,7 @@ class nsACString; namespace mozilla { class AbstractMediaDecoder; +class DecoderDoctorDiagnostics; class MediaDecoder; class MediaDecoderOwner; class MediaDecoderReader; @@ -35,7 +36,8 @@ public: // specified, pass false in aHaveRequestedCodecs. static CanPlayStatus CanHandleMediaType(const char* aMIMEType, bool aHaveRequestedCodecs, - const nsAString& aRequestedCodecs); + const nsAString& aRequestedCodecs, + DecoderDoctorDiagnostics* aDiagnostics); // Returns the CanPlayStatus indicating if we can handle this MIME type and // codecs type natively, excluding any plugins-based reader (such as @@ -43,18 +45,21 @@ public: // The MIME type should not include the codecs parameter. // That parameter is passed in aRequestedCodecs. static CanPlayStatus CanHandleCodecsType(const char* aMIMEType, - const nsAString& aRequestedCodecs); + const nsAString& aRequestedCodecs, + DecoderDoctorDiagnostics* aDiagnostics); // Returns true if we should handle this MIME type when it appears // as an or as a toplevel page. If, in practice, our support // for the type is more limited than appears in the wild, we should return // false here even if CanHandleMediaType would return true. - static bool ShouldHandleMediaType(const char* aMIMEType); + static bool ShouldHandleMediaType(const char* aMIMEType, + DecoderDoctorDiagnostics* aDiagnostics); // Create a decoder for the given aType. Returns null if we // were unable to create the decoder. static already_AddRefed CreateDecoder(const nsACString& aType, - MediaDecoderOwner* aOwner); + MediaDecoderOwner* aOwner, + DecoderDoctorDiagnostics* aDiagnostics); // Create a reader for thew given MIME type aType. Returns null // if we were unable to create the reader. @@ -72,7 +77,8 @@ public: static bool IsWebMTypeAndEnabled(const nsACString& aType); static bool IsWebMAudioType(const nsACString& aType); - static bool IsMP4TypeAndEnabled(const nsACString& aType); + static bool IsMP4TypeAndEnabled(const nsACString& aType, + DecoderDoctorDiagnostics* aDiagnostics); }; } // namespace mozilla diff --git a/dom/media/MP3Decoder.cpp b/dom/media/MP3Decoder.cpp index 42500a8518..c3cfd229ad 100644 --- a/dom/media/MP3Decoder.cpp +++ b/dom/media/MP3Decoder.cpp @@ -34,7 +34,8 @@ bool MP3Decoder::IsEnabled() { PDMFactory::Init(); RefPtr platform = new PDMFactory(); - return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mpeg")); + return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mpeg"), + /* DecoderDoctorDiagnostics* */ nullptr); } /* static */ diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index ed6236cf91..84c0e10e85 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -30,6 +30,10 @@ #include "nsPrintfCString.h" #include "mozilla/Telemetry.h" +#ifdef MOZ_ANDROID_OMX +#include "AndroidBridge.h" +#endif + using namespace mozilla::dom; using namespace mozilla::layers; using namespace mozilla::media; @@ -1717,9 +1721,11 @@ MediaDecoder::IsOmxEnabled() #ifdef MOZ_ANDROID_OMX bool -MediaDecoder::IsAndroidMediaEnabled() +MediaDecoder::IsAndroidMediaPluginEnabled() { - return Preferences::GetBool("media.plugins.enabled"); + return AndroidBridge::Bridge() && + AndroidBridge::Bridge()->GetAPIVersion() < 16 && + Preferences::GetBool("media.plugins.enabled"); } #endif diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index 99a9d27057..23ce9b3c32 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -637,7 +637,7 @@ private: #endif #ifdef MOZ_ANDROID_OMX - static bool IsAndroidMediaEnabled(); + static bool IsAndroidMediaPluginEnabled(); #endif #ifdef MOZ_WMF diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index d7cb820aa1..b50624a0d8 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -395,7 +395,8 @@ MediaFormatReader::EnsureDecoderCreated(TrackType aTrack) *decoder.mInfo->GetAsAudioInfo() : mInfo.mAudio, decoder.mTaskQueue, - decoder.mCallback); + decoder.mCallback, + /* DecoderDoctorDiagnostics* */ nullptr); break; case TrackType::kVideoTrack: // Decoders use the layers backend to decide if they can use hardware decoding, @@ -406,6 +407,7 @@ MediaFormatReader::EnsureDecoderCreated(TrackType aTrack) mInfo.mVideo, decoder.mTaskQueue, decoder.mCallback, + /* DecoderDoctorDiagnostics* */ nullptr, mLayersBackendType, GetImageContainer()); break; diff --git a/dom/media/fmp4/MP4Decoder.cpp b/dom/media/fmp4/MP4Decoder.cpp index ef27308a8c..a7feb79fe1 100644 --- a/dom/media/fmp4/MP4Decoder.cpp +++ b/dom/media/fmp4/MP4Decoder.cpp @@ -92,7 +92,8 @@ IsWhitelistedH264Codec(const nsAString& aCodec) /* static */ bool MP4Decoder::CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs, - const nsAString& aCodecs) + const nsAString& aCodecs, + DecoderDoctorDiagnostics* aDiagnostics) { if (!IsEnabled()) { return false; @@ -155,7 +156,7 @@ MP4Decoder::CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs, PDMFactory::Init(); RefPtr platform = new PDMFactory(); for (const nsCString& codecMime : codecMimes) { - if (!platform->SupportsMimeType(codecMime)) { + if (!platform->SupportsMimeType(codecMime, aDiagnostics)) { return false; } } @@ -164,7 +165,8 @@ MP4Decoder::CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs, } /* static */ bool -MP4Decoder::CanHandleMediaType(const nsAString& aContentType) +MP4Decoder::CanHandleMediaType(const nsAString& aContentType, + DecoderDoctorDiagnostics* aDiagnostics) { nsContentTypeParser parser(aContentType); nsAutoString mimeType; @@ -175,7 +177,9 @@ MP4Decoder::CanHandleMediaType(const nsAString& aContentType) nsString codecs; parser.GetParameter("codecs", codecs); - return CanHandleMediaType(NS_ConvertUTF16toUTF8(mimeType), codecs); + return CanHandleMediaType(NS_ConvertUTF16toUTF8(mimeType), + codecs, + aDiagnostics); } /* static */ @@ -233,7 +237,9 @@ CreateTestH264Decoder(layers::LayersBackend aBackend, RefPtr platform = new PDMFactory(); RefPtr decoder( - platform->CreateDecoder(aConfig, aTaskQueue, nullptr, aBackend, nullptr)); + platform->CreateDecoder(aConfig, aTaskQueue, nullptr, + /* DecoderDoctorDiagnostics* */ nullptr, + aBackend, nullptr)); return decoder.forget(); } diff --git a/dom/media/fmp4/MP4Decoder.h b/dom/media/fmp4/MP4Decoder.h index d99eb8e976..f5ad54186b 100644 --- a/dom/media/fmp4/MP4Decoder.h +++ b/dom/media/fmp4/MP4Decoder.h @@ -32,9 +32,11 @@ public: // with a comma-delimited list of codecs to check support for. Notes in // out params wether the codecs string contains AAC or H.264. static bool CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs, - const nsAString& aCodecs); + const nsAString& aCodecs, + DecoderDoctorDiagnostics* aDiagnostics); - static bool CanHandleMediaType(const nsAString& aMIMEType); + static bool CanHandleMediaType(const nsAString& aMIMEType, + DecoderDoctorDiagnostics* aDiagnostics); // Returns true if the MP4 backend is preffed on. static bool IsEnabled(); diff --git a/dom/media/gtest/TestMediaDataDecoder.cpp b/dom/media/gtest/TestMediaDataDecoder.cpp index 8e37525ca5..47e78bf7ed 100644 --- a/dom/media/gtest/TestMediaDataDecoder.cpp +++ b/dom/media/gtest/TestMediaDataDecoder.cpp @@ -42,7 +42,8 @@ private: TEST(MediaDataDecoder, H264) { - if (!DecoderTraits::IsMP4TypeAndEnabled(NS_LITERAL_CSTRING("video/mp4"))) { + if (!DecoderTraits::IsMP4TypeAndEnabled(NS_LITERAL_CSTRING("video/mp4") + , /* DecoderDoctorDiagnostics* */ nullptr)) { EXPECT_TRUE(true); } else { RefPtr resource = diff --git a/dom/media/mediasource/MediaSource.cpp b/dom/media/mediasource/MediaSource.cpp index ef78837ac6..15f3d7fdb0 100644 --- a/dom/media/mediasource/MediaSource.cpp +++ b/dom/media/mediasource/MediaSource.cpp @@ -7,7 +7,9 @@ #include "MediaSource.h" #include "AsyncEventRunner.h" +#include "DecoderDoctorDiagnostics.h" #include "DecoderTraits.h" +#include "DecoderDoctorDiagnostics.h" #include "MediaSourceUtils.h" #include "SourceBuffer.h" #include "SourceBufferList.h" @@ -73,25 +75,26 @@ static const char* const gMediaSourceTypes[6] = { // optional "Windows Media Feature Pack" // 2. If H264 hardware acceleration is not available. static bool -IsWebMForced() +IsWebMForced(DecoderDoctorDiagnostics* aDiagnostics) { bool mp4supported = - DecoderTraits::IsMP4TypeAndEnabled(NS_LITERAL_CSTRING("video/mp4")); + DecoderTraits::IsMP4TypeAndEnabled(NS_LITERAL_CSTRING("video/mp4"), + aDiagnostics); bool hwsupported = gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding(); return !mp4supported || !hwsupported; } static nsresult -IsTypeSupported(const nsAString& aType) +IsTypeSupported(const nsAString& aType, DecoderDoctorDiagnostics* aDiagnostics) { if (aType.IsEmpty()) { - return NS_ERROR_DOM_TYPE_ERR; + return NS_ERROR_DOM_INVALID_ACCESS_ERR; } nsContentTypeParser parser(aType); nsAutoString mimeType; nsresult rv = parser.GetType(mimeType); if (NS_FAILED(rv)) { - return NS_ERROR_DOM_INVALID_STATE_ERR; + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType); @@ -100,27 +103,35 @@ IsTypeSupported(const nsAString& aType) for (uint32_t i = 0; gMediaSourceTypes[i]; ++i) { if (mimeType.EqualsASCII(gMediaSourceTypes[i])) { - if (DecoderTraits::IsMP4TypeAndEnabled(mimeTypeUTF8)) { + if (DecoderTraits::IsMP4TypeAndEnabled(mimeTypeUTF8, aDiagnostics)) { if (!Preferences::GetBool("media.mediasource.mp4.enabled", false)) { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } if (hasCodecs && DecoderTraits::CanHandleCodecsType(mimeTypeUTF8.get(), - codecs) == CANPLAY_NO) { - return NS_ERROR_DOM_INVALID_STATE_ERR; + codecs, + aDiagnostics) == CANPLAY_NO) { + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } + if (aDiagnostics) { + aDiagnostics->SetCanPlay(); } return NS_OK; } else if (DecoderTraits::IsWebMTypeAndEnabled(mimeTypeUTF8)) { if (!(Preferences::GetBool("media.mediasource.webm.enabled", false) || (Preferences::GetBool("media.mediasource.webm.audio.enabled", true) && DecoderTraits::IsWebMAudioType(mimeTypeUTF8)) || - IsWebMForced())) { + IsWebMForced(aDiagnostics))) { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } if (hasCodecs && DecoderTraits::CanHandleCodecsType(mimeTypeUTF8.get(), - codecs) == CANPLAY_NO) { - return NS_ERROR_DOM_INVALID_STATE_ERR; + codecs, + aDiagnostics) == CANPLAY_NO) { + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } + if (aDiagnostics) { + aDiagnostics->SetCanPlay(); } return NS_OK; } @@ -194,7 +205,7 @@ MediaSource::SetDuration(double aDuration, ErrorResult& aRv) MOZ_ASSERT(NS_IsMainThread()); MSE_API("SetDuration(aDuration=%f, ErrorResult)", aDuration); if (aDuration < 0 || IsNaN(aDuration)) { - aRv.Throw(NS_ERROR_DOM_TYPE_ERR); + aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); return; } if (mReadyState != MediaSourceReadyState::Open || @@ -217,7 +228,18 @@ already_AddRefed MediaSource::AddSourceBuffer(const nsAString& aType, ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread()); - nsresult rv = mozilla::IsTypeSupported(aType); + DecoderDoctorDiagnostics diagnostics; + nsresult rv = mozilla::IsTypeSupported(aType, &diagnostics); + if (NS_SUCCEEDED(rv)) { + diagnostics.SetCanPlay(); + } + if (GetOwner()) { + diagnostics.StoreDiagnostics(GetOwner()->GetExtantDoc(), aType, + "AddSourceBuffer with owner window's doc"); + } else { + diagnostics.StoreDiagnostics(nullptr, aType, + "AddSourceBuffer with nothing"); + } MSE_API("AddSourceBuffer(aType=%s)%s", NS_ConvertUTF16toUTF8(aType).get(), rv == NS_OK ? "" : " [not supported]"); @@ -328,15 +350,27 @@ MediaSource::EndOfStream(const Optional& aError, Er mDecoder->DecodeError(); break; default: - aRv.Throw(NS_ERROR_DOM_TYPE_ERR); + aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); } } /* static */ bool -MediaSource::IsTypeSupported(const GlobalObject&, const nsAString& aType) +MediaSource::IsTypeSupported(const GlobalObject& aOwner, const nsAString& aType) { MOZ_ASSERT(NS_IsMainThread()); - nsresult rv = mozilla::IsTypeSupported(aType); + DecoderDoctorDiagnostics diagnostics; + nsresult rv = mozilla::IsTypeSupported(aType, &diagnostics); + if (NS_SUCCEEDED(rv)) { + diagnostics.SetCanPlay(); + } + nsCOMPtr window = do_QueryInterface(aOwner.GetAsSupports()); + if (window) { + diagnostics.StoreDiagnostics(window->GetExtantDoc(), aType, + "IsTypeSupported with aOwner window's doc"); + } else { + diagnostics.StoreDiagnostics(nullptr, aType, + "IsTypeSupported with nothing"); + } #define this nullptr MSE_API("IsTypeSupported(aType=%s)%s ", NS_ConvertUTF16toUTF8(aType).get(), rv == NS_OK ? "OK" : "[not supported]"); diff --git a/dom/media/moz.build b/dom/media/moz.build index 685b276e4e..73cd674346 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -96,6 +96,7 @@ EXPORTS += [ 'AudioStream.h', 'BufferMediaResource.h', 'CubebUtils.h', + 'DecoderDoctorDiagnostics.h', 'DecoderTraits.h', 'DOMMediaStream.h', 'EncodedBufferCache.h', @@ -202,6 +203,7 @@ UNIFIED_SOURCES += [ 'AudioTrackList.cpp', 'CanvasCaptureMediaStream.cpp', 'CubebUtils.cpp', + 'DecoderDoctorDiagnostics.cpp', 'DOMMediaStream.cpp', 'EncodedBufferCache.cpp', 'FileBlockCache.cpp', diff --git a/dom/media/platforms/PDMFactory.cpp b/dom/media/platforms/PDMFactory.cpp index b3a6e52241..3caa8217f2 100644 --- a/dom/media/platforms/PDMFactory.cpp +++ b/dom/media/platforms/PDMFactory.cpp @@ -131,6 +131,7 @@ already_AddRefed PDMFactory::CreateDecoder(const TrackInfo& aConfig, FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics, layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer) { @@ -141,12 +142,13 @@ PDMFactory::CreateDecoder(const TrackInfo& aConfig, aConfig, aTaskQueue, aCallback, + aDiagnostics, aLayersBackend, aImageContainer); } for (auto& current : mCurrentPDMs) { - if (!current->SupportsMimeType(aConfig.mMimeType)) { + if (!current->SupportsMimeType(aConfig.mMimeType, aDiagnostics)) { continue; } RefPtr m = @@ -154,6 +156,7 @@ PDMFactory::CreateDecoder(const TrackInfo& aConfig, aConfig, aTaskQueue, aCallback, + aDiagnostics, aLayersBackend, aImageContainer); if (m) { @@ -169,6 +172,7 @@ PDMFactory::CreateDecoderWithPDM(PlatformDecoderModule* aPDM, const TrackInfo& aConfig, FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics, layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer) { @@ -178,7 +182,8 @@ PDMFactory::CreateDecoderWithPDM(PlatformDecoderModule* aPDM, if (aConfig.GetAsAudioInfo()) { m = aPDM->CreateAudioDecoder(*aConfig.GetAsAudioInfo(), aTaskQueue, - aCallback); + aCallback, + aDiagnostics); return m.forget(); } @@ -203,7 +208,8 @@ PDMFactory::CreateDecoderWithPDM(PlatformDecoderModule* aPDM, aLayersBackend, aImageContainer, aTaskQueue, - callback); + callback, + aDiagnostics); const nsresult rv = h->GetLastError(); if (NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_INITIALIZED) { // The H264Converter either successfully created the wrapped decoder, @@ -216,7 +222,8 @@ PDMFactory::CreateDecoderWithPDM(PlatformDecoderModule* aPDM, aLayersBackend, aImageContainer, aTaskQueue, - callback); + callback, + aDiagnostics); } if (callbackWrapper && m) { @@ -227,12 +234,13 @@ PDMFactory::CreateDecoderWithPDM(PlatformDecoderModule* aPDM, } bool -PDMFactory::SupportsMimeType(const nsACString& aMimeType) const +PDMFactory::SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const { if (mEMEPDM) { - return mEMEPDM->SupportsMimeType(aMimeType); + return mEMEPDM->SupportsMimeType(aMimeType, aDiagnostics); } - RefPtr current = GetDecoder(aMimeType); + RefPtr current = GetDecoder(aMimeType, aDiagnostics); return !!current; } @@ -305,11 +313,12 @@ PDMFactory::StartupPDM(PlatformDecoderModule* aPDM) } already_AddRefed -PDMFactory::GetDecoder(const nsACString& aMimeType) const +PDMFactory::GetDecoder(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const { RefPtr pdm; for (auto& current : mCurrentPDMs) { - if (current->SupportsMimeType(aMimeType)) { + if (current->SupportsMimeType(aMimeType, aDiagnostics)) { pdm = current; break; } diff --git a/dom/media/platforms/PDMFactory.h b/dom/media/platforms/PDMFactory.h index f85bd2ca96..3b128da11d 100644 --- a/dom/media/platforms/PDMFactory.h +++ b/dom/media/platforms/PDMFactory.h @@ -13,6 +13,8 @@ class CDMProxy; namespace mozilla { +class DecoderDoctorDiagnostics; + class PDMFactory final { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PDMFactory) @@ -32,10 +34,12 @@ public: CreateDecoder(const TrackInfo& aConfig, FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics, layers::LayersBackend aLayersBackend = layers::LayersBackend::LAYERS_NONE, layers::ImageContainer* aImageContainer = nullptr); - bool SupportsMimeType(const nsACString& aMimeType) const; + bool SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const; #ifdef MOZ_EME // Creates a PlatformDecoderModule that uses a CDMProxy to decrypt or @@ -53,13 +57,15 @@ private: bool StartupPDM(PlatformDecoderModule* aPDM); // Returns the first PDM in our list supporting the mimetype. already_AddRefed - GetDecoder(const nsACString& aMimeType) const; + GetDecoder(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const; already_AddRefed CreateDecoderWithPDM(PlatformDecoderModule* aPDM, const TrackInfo& aConfig, FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics, layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer); diff --git a/dom/media/platforms/PlatformDecoderModule.h b/dom/media/platforms/PlatformDecoderModule.h index c6ad46306d..2992ed6f8c 100644 --- a/dom/media/platforms/PlatformDecoderModule.h +++ b/dom/media/platforms/PlatformDecoderModule.h @@ -20,6 +20,7 @@ class TrackInfo; class AudioInfo; class VideoInfo; class MediaRawData; +class DecoderDoctorDiagnostics; namespace layers { class ImageContainer; @@ -53,7 +54,8 @@ public: virtual nsresult Startup() { return NS_OK; }; // Indicates if the PlatformDecoderModule supports decoding of aMimeType. - virtual bool SupportsMimeType(const nsACString& aMimeType) const = 0; + virtual bool SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const = 0; enum ConversionRequired { kNeedNone, @@ -89,7 +91,8 @@ protected: layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) = 0; + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) = 0; // Creates an Audio decoder with the specified properties. // Asynchronous decoding of audio should be done in runnables dispatched to @@ -104,7 +107,8 @@ protected: virtual already_AddRefed CreateAudioDecoder(const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) = 0; + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) = 0; }; // A callback used by MediaDataDecoder to return output/errors to the diff --git a/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp b/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp index 5d27b2f078..b38659abcd 100644 --- a/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp +++ b/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp @@ -13,7 +13,8 @@ namespace mozilla { bool -AgnosticDecoderModule::SupportsMimeType(const nsACString& aMimeType) const +AgnosticDecoderModule::SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const { return VPXDecoder::IsVPX(aMimeType) || OpusDataDecoder::IsOpus(aMimeType) || @@ -26,7 +27,8 @@ AgnosticDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig, layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) { RefPtr m; @@ -43,7 +45,8 @@ AgnosticDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig, already_AddRefed AgnosticDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) { RefPtr m; diff --git a/dom/media/platforms/agnostic/AgnosticDecoderModule.h b/dom/media/platforms/agnostic/AgnosticDecoderModule.h index ff0d0583c7..7b9fe6c3d5 100644 --- a/dom/media/platforms/agnostic/AgnosticDecoderModule.h +++ b/dom/media/platforms/agnostic/AgnosticDecoderModule.h @@ -10,7 +10,8 @@ public: AgnosticDecoderModule() = default; virtual ~AgnosticDecoderModule() = default; - bool SupportsMimeType(const nsACString& aMimeType) const override; + bool SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const override; ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override @@ -25,13 +26,15 @@ protected: layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) override; + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override; // Decode thread. already_AddRefed CreateAudioDecoder(const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) override; + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override; }; } // namespace mozilla diff --git a/dom/media/platforms/agnostic/BlankDecoderModule.cpp b/dom/media/platforms/agnostic/BlankDecoderModule.cpp index cedceb0b7c..d19840dc49 100644 --- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp +++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp @@ -230,7 +230,8 @@ public: layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) override { + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override { BlankVideoDataCreator* creator = new BlankVideoDataCreator( aConfig.mDisplay.width, aConfig.mDisplay.height, aImageContainer); RefPtr decoder = @@ -245,7 +246,8 @@ public: already_AddRefed CreateAudioDecoder(const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) override { + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override { BlankAudioDataCreator* creator = new BlankAudioDataCreator( aConfig.mChannels, aConfig.mRate); @@ -258,7 +260,8 @@ public: } bool - SupportsMimeType(const nsACString& aMimeType) const override + SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const override { return true; } diff --git a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp index aa7da132e9..1bce0b4092 100644 --- a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp +++ b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp @@ -51,7 +51,8 @@ GMPDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig, layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) { if (!aConfig.mMimeType.EqualsLiteral("video/avc")) { return nullptr; @@ -69,7 +70,8 @@ GMPDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig, already_AddRefed GMPDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) { if (!aConfig.mMimeType.EqualsLiteral("audio/mp4a-latm")) { return nullptr; @@ -105,11 +107,13 @@ HasGMPFor(const nsACString& aAPI, if (aGMP.EqualsLiteral("org.w3.clearkey")) { RefPtr pdm(new WMFDecoderModule()); if (aCodec.EqualsLiteral("aac") && - !pdm->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm"))) { + !pdm->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm"), + /* DecoderDoctorDiagnostics* */ nullptr)) { return false; } if (aCodec.EqualsLiteral("h264") && - !pdm->SupportsMimeType(NS_LITERAL_CSTRING("video/avc"))) { + !pdm->SupportsMimeType(NS_LITERAL_CSTRING("video/avc"), + /* DecoderDoctorDiagnostics* */ nullptr)) { return false; } } @@ -228,7 +232,8 @@ GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType, } bool -GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType) const +GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const { return SupportsMimeType(aMimeType, PreferredGMP(aMimeType)); } diff --git a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h index ef5a0f5532..799271e55a 100644 --- a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h +++ b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h @@ -24,19 +24,22 @@ public: layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) override; + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override; // Decode thread. already_AddRefed CreateAudioDecoder(const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) override; + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override; ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override; bool - SupportsMimeType(const nsACString& aMimeType) const override; + SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const override; // Main thread only. static void Init(); diff --git a/dom/media/platforms/android/AndroidDecoderModule.cpp b/dom/media/platforms/android/AndroidDecoderModule.cpp index 0c45f88800..1d035749bb 100644 --- a/dom/media/platforms/android/AndroidDecoderModule.cpp +++ b/dom/media/platforms/android/AndroidDecoderModule.cpp @@ -325,7 +325,8 @@ public: }; bool -AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType) const +AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const { if (!AndroidBridge::Bridge() || AndroidBridge::Bridge()->GetAPIVersion() < 16) { @@ -363,7 +364,8 @@ already_AddRefed AndroidDecoderModule::CreateVideoDecoder( const VideoInfo& aConfig, layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) { MediaFormat::LocalRef format; @@ -382,7 +384,8 @@ AndroidDecoderModule::CreateVideoDecoder( already_AddRefed AndroidDecoderModule::CreateAudioDecoder( const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) { MOZ_ASSERT(aConfig.mBitDepth == 16, "We only handle 16-bit audio!"); diff --git a/dom/media/platforms/android/AndroidDecoderModule.h b/dom/media/platforms/android/AndroidDecoderModule.h index 06425d9746..5c86f20d21 100644 --- a/dom/media/platforms/android/AndroidDecoderModule.h +++ b/dom/media/platforms/android/AndroidDecoderModule.h @@ -25,18 +25,21 @@ public: layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) override; + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override; already_AddRefed CreateAudioDecoder(const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) override; + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override; AndroidDecoderModule() {} virtual ~AndroidDecoderModule() {} - bool SupportsMimeType(const nsACString& aMimeType) const override; + bool SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const override; ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override; diff --git a/dom/media/platforms/apple/AppleDecoderModule.cpp b/dom/media/platforms/apple/AppleDecoderModule.cpp index a1980c3fc9..41feab18e7 100644 --- a/dom/media/platforms/apple/AppleDecoderModule.cpp +++ b/dom/media/platforms/apple/AppleDecoderModule.cpp @@ -83,7 +83,8 @@ AppleDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig, layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) { RefPtr decoder; @@ -109,7 +110,8 @@ AppleDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig, already_AddRefed AppleDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) { RefPtr decoder = new AppleATDecoder(aConfig, aAudioTaskQueue, aCallback); @@ -117,7 +119,8 @@ AppleDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig, } bool -AppleDecoderModule::SupportsMimeType(const nsACString& aMimeType) const +AppleDecoderModule::SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const { return (sIsCoreMediaAvailable && (aMimeType.EqualsLiteral("audio/mpeg") || diff --git a/dom/media/platforms/apple/AppleDecoderModule.h b/dom/media/platforms/apple/AppleDecoderModule.h index 56d61b841f..355213597f 100644 --- a/dom/media/platforms/apple/AppleDecoderModule.h +++ b/dom/media/platforms/apple/AppleDecoderModule.h @@ -24,15 +24,18 @@ public: layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) override; + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override; // Decode thread. already_AddRefed CreateAudioDecoder(const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) override; + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override; - bool SupportsMimeType(const nsACString& aMimeType) const override; + bool SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const override; ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override; diff --git a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h index f81f150e56..84b404ee8a 100644 --- a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h +++ b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h @@ -34,7 +34,8 @@ public: layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) override + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override { RefPtr decoder = new FFmpegH264Decoder(aVideoTaskQueue, aCallback, aConfig, @@ -45,14 +46,16 @@ public: already_AddRefed CreateAudioDecoder(const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) override + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override { RefPtr decoder = new FFmpegAudioDecoder(aAudioTaskQueue, aCallback, aConfig); return decoder.forget(); } - bool SupportsMimeType(const nsACString& aMimeType) const override + bool SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const override { AVCodecID audioCodec = FFmpegAudioDecoder::GetCodecId(aMimeType); AVCodecID videoCodec = FFmpegH264Decoder::GetCodecId(aMimeType); diff --git a/dom/media/platforms/ffmpeg/README.mcp b/dom/media/platforms/ffmpeg/README.mozilla similarity index 100% rename from dom/media/platforms/ffmpeg/README.mcp rename to dom/media/platforms/ffmpeg/README.mozilla diff --git a/dom/media/platforms/gonk/GonkDecoderModule.cpp b/dom/media/platforms/gonk/GonkDecoderModule.cpp index 3856898301..55f067f5d7 100644 --- a/dom/media/platforms/gonk/GonkDecoderModule.cpp +++ b/dom/media/platforms/gonk/GonkDecoderModule.cpp @@ -31,7 +31,8 @@ GonkDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig, mozilla::layers::LayersBackend aLayersBackend, mozilla::layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) { RefPtr decoder = new GonkMediaDataDecoder(new GonkVideoDecoderManager(aImageContainer, aConfig), @@ -42,7 +43,8 @@ GonkDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig, already_AddRefed GonkDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) { RefPtr decoder = new GonkMediaDataDecoder(new GonkAudioDecoderManager(aConfig), @@ -61,7 +63,8 @@ GonkDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const } bool -GonkDecoderModule::SupportsMimeType(const nsACString& aMimeType) const +GonkDecoderModule::SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const { return aMimeType.EqualsLiteral("audio/mp4a-latm") || aMimeType.EqualsLiteral("audio/3gpp") || diff --git a/dom/media/platforms/gonk/GonkDecoderModule.h b/dom/media/platforms/gonk/GonkDecoderModule.h index 5034f3ca2b..69ac7a82a7 100644 --- a/dom/media/platforms/gonk/GonkDecoderModule.h +++ b/dom/media/platforms/gonk/GonkDecoderModule.h @@ -22,20 +22,23 @@ public: mozilla::layers::LayersBackend aLayersBackend, mozilla::layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) override; + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override; // Decode thread. already_AddRefed CreateAudioDecoder(const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) override; + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override; static void Init(); ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override; - bool SupportsMimeType(const nsACString& aMimeType) const override; + bool SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const override; }; diff --git a/dom/media/platforms/omx/OmxPlatformLayer.cpp b/dom/media/platforms/omx/OmxPlatformLayer.cpp new file mode 100644 index 0000000000..a3a3a226c0 --- /dev/null +++ b/dom/media/platforms/omx/OmxPlatformLayer.cpp @@ -0,0 +1,212 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "OmxPlatformLayer.h" + +#if defined(MOZ_WIDGET_GONK) && (ANDROID_VERSION == 20 || ANDROID_VERSION == 19) +#define OMX_PLATFORM_GONK +#include "GonkOmxPlatformLayer.h" +#endif + +extern mozilla::LogModule* GetPDMLog(); + +#ifdef LOG +#undef LOG +#endif + +#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("OmxPlatformLayer -- %s: " arg, __func__, ##__VA_ARGS__)) + +#define RETURN_IF_ERR(err) \ + if (err != OMX_ErrorNone) { \ + LOG("error: 0x%08x", err); \ + return err; \ + } \ + +// Common OMX decoder configuration code. +namespace mozilla { + +// This helper class encapsulates the details of component parameters setting +// for different OMX audio & video codecs. +template +class OmxConfig +{ +public: + virtual ~OmxConfig() {} + // Subclasses should implement this method to configure the codec. + virtual OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const ParamType& aParam) = 0; +}; + +typedef OmxConfig OmxAudioConfig; +typedef OmxConfig OmxVideoConfig; + +template +UniquePtr ConfigForMime(const nsACString&); + +class OmxAacConfig : public OmxAudioConfig +{ +public: + OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const AudioInfo& aInfo) override + { + OMX_ERRORTYPE err; + + OMX_AUDIO_PARAM_AACPROFILETYPE aacProfile; + InitOmxParameter(&aacProfile); + err = aOmx.GetParameter(OMX_IndexParamAudioAac, &aacProfile, sizeof(aacProfile)); + RETURN_IF_ERR(err); + + aacProfile.nChannels = aInfo.mChannels; + aacProfile.nSampleRate = aInfo.mRate; + aacProfile.eAACProfile = static_cast(aInfo.mProfile); + err = aOmx.SetParameter(OMX_IndexParamAudioAac, &aacProfile, sizeof(aacProfile)); + RETURN_IF_ERR(err); + + LOG("Config OMX_IndexParamAudioAac, channel %d, sample rate %d, profile %d", + aacProfile.nChannels, aacProfile.nSampleRate, aacProfile.eAACProfile); + + return OMX_ErrorNone; + } +}; + +template<> +UniquePtr +ConfigForMime(const nsACString& aMimeType) +{ + UniquePtr conf; + + if (OmxPlatformLayer::SupportsMimeType(aMimeType)) { + if (aMimeType.EqualsLiteral("audio/mp4a-latm")) { + conf.reset(new OmxAacConfig()); + } + } + return Move(conf); +} + +// There should be a better way to calculate it. +#define MIN_VIDEO_INPUT_BUFFER_SIZE 64 * 1024 + +class OmxCommonVideoConfig : public OmxVideoConfig +{ +public: + explicit OmxCommonVideoConfig(OMX_VIDEO_CODINGTYPE aCodec) + : OmxVideoConfig() + , mCodec(aCodec) + {} + + OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const VideoInfo& aInfo) override + { + OMX_ERRORTYPE err; + OMX_PARAM_PORTDEFINITIONTYPE def; + + // Set up in/out port definition. + nsTArray ports; + GetOmxPortIndex(ports); + for (auto idx : ports) { + InitOmxParameter(&def); + def.nPortIndex = idx; + err = aOmx.GetParameter(OMX_IndexParamPortDefinition, &def, sizeof(def)); + RETURN_IF_ERR(err); + + def.format.video.nFrameWidth = aInfo.mDisplay.width; + def.format.video.nFrameHeight = aInfo.mDisplay.height; + def.format.video.nStride = aInfo.mImage.width; + def.format.video.nSliceHeight = aInfo.mImage.height; + + if (def.eDir == OMX_DirInput) { + def.format.video.eCompressionFormat = mCodec; + def.format.video.eColorFormat = OMX_COLOR_FormatUnused; + if (def.nBufferSize < MIN_VIDEO_INPUT_BUFFER_SIZE) { + def.nBufferSize = aInfo.mImage.width * aInfo.mImage.height; + LOG("Change input buffer size to %d", def.nBufferSize); + } + } else { + def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; + } + + err = aOmx.SetParameter(OMX_IndexParamPortDefinition, &def, sizeof(def)); + } + return err; + } + +private: + const OMX_VIDEO_CODINGTYPE mCodec; +}; + +template<> +UniquePtr +ConfigForMime(const nsACString& aMimeType) +{ + UniquePtr conf; + + if (OmxPlatformLayer::SupportsMimeType(aMimeType)) { + if (aMimeType.EqualsLiteral("video/avc")) { + conf.reset(new OmxCommonVideoConfig(OMX_VIDEO_CodingAVC)); + } else if (aMimeType.EqualsLiteral("video/mp4v-es") || + aMimeType.EqualsLiteral("video/mp4")) { + conf.reset(new OmxCommonVideoConfig(OMX_VIDEO_CodingMPEG4)); + } else if (aMimeType.EqualsLiteral("video/3gpp")) { + conf.reset(new OmxCommonVideoConfig(OMX_VIDEO_CodingH263)); + } + } + return Move(conf); +} + +OMX_ERRORTYPE +OmxPlatformLayer::Config() +{ + MOZ_ASSERT(mInfo); + + if (mInfo->IsAudio()) { + UniquePtr conf(ConfigForMime(mInfo->mMimeType)); + MOZ_ASSERT(conf.get()); + return conf->Apply(*this, *(mInfo->GetAsAudioInfo())); + } else if (mInfo->IsVideo()) { + UniquePtr conf(ConfigForMime(mInfo->mMimeType)); + MOZ_ASSERT(conf.get()); + return conf->Apply(*this, *(mInfo->GetAsVideoInfo())); + } else { + MOZ_ASSERT_UNREACHABLE("non-AV data (text?) is not supported."); + return OMX_ErrorNotImplemented; + } +} + +// Implementations for different platforms will be defined in their own files. +#ifdef OMX_PLATFORM_GONK + +bool +OmxPlatformLayer::SupportsMimeType(const nsACString& aMimeType) +{ + return GonkOmxPlatformLayer::FindComponents(aMimeType); +} + +OmxPlatformLayer* +OmxPlatformLayer::Create(OmxDataDecoder* aDataDecoder, + OmxPromiseLayer* aPromiseLayer, + TaskQueue* aTaskQueue, + layers::ImageContainer* aImageContainer) +{ + return new GonkOmxPlatformLayer(aDataDecoder, aPromiseLayer, aTaskQueue, aImageContainer); +} + +#else // For platforms without OMX IL support. + +bool +OmxPlatformLayer::SupportsMimeType(const nsACString& aMimeType) +{ + return false; +} + +OmxPlatformLayer* +OmxPlatformLayer::Create(OmxDataDecoder* aDataDecoder, + OmxPromiseLayer* aPromiseLayer, + TaskQueue* aTaskQueue, + layers::ImageContainer* aImageContainer) +{ + return nullptr; +} + +#endif + +} diff --git a/dom/media/platforms/wmf/WMFDecoderModule.cpp b/dom/media/platforms/wmf/WMFDecoderModule.cpp index 5914fde3ab..d45be3d4a5 100644 --- a/dom/media/platforms/wmf/WMFDecoderModule.cpp +++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp @@ -101,7 +101,8 @@ WMFDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig, layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) { nsAutoPtr manager( new WMFVideoMFTManager(aConfig, @@ -122,7 +123,8 @@ WMFDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig, already_AddRefed WMFDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) { nsAutoPtr manager(new WMFAudioMFTManager(aConfig)); @@ -176,7 +178,8 @@ WMFDecoderModule::HasAAC() } bool -WMFDecoderModule::SupportsMimeType(const nsACString& aMimeType) const +WMFDecoderModule::SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const { if ((aMimeType.EqualsLiteral("audio/mp4a-latm") || aMimeType.EqualsLiteral("audio/mp4")) && diff --git a/dom/media/platforms/wmf/WMFDecoderModule.h b/dom/media/platforms/wmf/WMFDecoderModule.h index 67ec7ac6de..d2b73deeeb 100644 --- a/dom/media/platforms/wmf/WMFDecoderModule.h +++ b/dom/media/platforms/wmf/WMFDecoderModule.h @@ -24,14 +24,17 @@ public: layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) override; + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override; already_AddRefed CreateAudioDecoder(const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) override; + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) override; - bool SupportsMimeType(const nsACString& aMimeType) const override; + bool SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const override; ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override; diff --git a/dom/media/platforms/wrappers/H264Converter.cpp b/dom/media/platforms/wrappers/H264Converter.cpp index 2a6bc6ac87..38bbc7d20b 100644 --- a/dom/media/platforms/wrappers/H264Converter.cpp +++ b/dom/media/platforms/wrappers/H264Converter.cpp @@ -20,7 +20,8 @@ H264Converter::H264Converter(PlatformDecoderModule* aPDM, layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics) : mPDM(aPDM) , mOriginalConfig(aConfig) , mCurrentConfig(aConfig) @@ -32,7 +33,7 @@ H264Converter::H264Converter(PlatformDecoderModule* aPDM, , mNeedAVCC(aPDM->DecoderNeedsConversion(aConfig) == PlatformDecoderModule::kNeedAVCC) , mLastError(NS_OK) { - CreateDecoder(); + CreateDecoder(aDiagnostics); } H264Converter::~H264Converter() @@ -132,7 +133,7 @@ H264Converter::IsHardwareAccelerated(nsACString& aFailureReason) const } nsresult -H264Converter::CreateDecoder() +H264Converter::CreateDecoder(DecoderDoctorDiagnostics* aDiagnostics) { if (mNeedAVCC && !mp4_demuxer::AnnexB::HasSPS(mCurrentConfig.mExtraData)) { // nothing found yet, will try again later @@ -149,7 +150,8 @@ H264Converter::CreateDecoder() mLayersBackend, mImageContainer, mVideoTaskQueue, - mCallback); + mCallback, + aDiagnostics); if (!mDecoder) { mLastError = NS_ERROR_FAILURE; return NS_ERROR_FAILURE; @@ -167,7 +169,7 @@ H264Converter::CreateDecoderAndInit(MediaRawData* aSample) } UpdateConfigFromExtraData(extra_data); - nsresult rv = CreateDecoder(); + nsresult rv = CreateDecoder(/* DecoderDoctorDiagnostics* */ nullptr); if (NS_SUCCEEDED(rv)) { // Queue the incoming sample. diff --git a/dom/media/platforms/wrappers/H264Converter.h b/dom/media/platforms/wrappers/H264Converter.h index 56d2f6da01..4eb0627841 100644 --- a/dom/media/platforms/wrappers/H264Converter.h +++ b/dom/media/platforms/wrappers/H264Converter.h @@ -26,7 +26,8 @@ public: layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback); + MediaDataDecoderCallback* aCallback, + DecoderDoctorDiagnostics* aDiagnostics); virtual ~H264Converter(); RefPtr Init() override; @@ -51,7 +52,7 @@ private: // Will create the required MediaDataDecoder if need AVCC and we have a SPS NAL. // Returns NS_ERROR_FAILURE if error is permanent and can't be recovered and // will set mError accordingly. - nsresult CreateDecoder(); + nsresult CreateDecoder(DecoderDoctorDiagnostics* aDiagnostics); nsresult CreateDecoderAndInit(MediaRawData* aSample); nsresult CheckForSPSChange(MediaRawData* aSample); void UpdateConfigFromExtraData(MediaByteBuffer* aExtraData); diff --git a/dom/media/wave/WaveDecoder.cpp b/dom/media/wave/WaveDecoder.cpp index 1962825a67..e7fff4ca08 100644 --- a/dom/media/wave/WaveDecoder.cpp +++ b/dom/media/wave/WaveDecoder.cpp @@ -42,7 +42,8 @@ WaveDecoder::IsEnabled() } PDMFactory::Init(); RefPtr platform = new PDMFactory(); - return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/x-wav")); + return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/x-wav"), + /* DecoderDoctorDiagnostics* */ nullptr); } /* static */ diff --git a/dom/tests/mochitest/dom-level2-core/mochitest.ini b/dom/tests/mochitest/dom-level2-core/mochitest.ini index 120f395040..26acc78e59 100644 --- a/dom/tests/mochitest/dom-level2-core/mochitest.ini +++ b/dom/tests/mochitest/dom-level2-core/mochitest.ini @@ -193,7 +193,6 @@ support-files = [test_importNode17.html] [test_localName01.html] [test_localName02.html] -[test_localName03.html] [test_localName04.html] [test_namednodemapgetnameditemns01.html] [test_namednodemapgetnameditemns02.html] @@ -240,7 +239,6 @@ support-files = [test_ownerElement01.html] [test_ownerElement02.html] [test_prefix01.html] -[test_prefix02.html] [test_prefix03.html] [test_prefix04.html] [test_publicId01.html] diff --git a/dom/tests/mochitest/dom-level2-core/test_localName03.html b/dom/tests/mochitest/dom-level2-core/test_localName03.html deleted file mode 100644 index ea4551d7e9..0000000000 --- a/dom/tests/mochitest/dom-level2-core/test_localName03.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/localName03 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/localName03

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/dom-level2-core/test_prefix02.html b/dom/tests/mochitest/dom-level2-core/test_prefix02.html deleted file mode 100644 index 9d3a45bfd1..0000000000 --- a/dom/tests/mochitest/dom-level2-core/test_prefix02.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/prefix02 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/prefix02

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index fbdadfffeb..0b518b9809 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -822,7 +822,7 @@ var interfaceNamesInGlobalScope = // IMPORTANT: Do not change this list without review from a DOM peer! {name: "MozOtaStatusEvent", b2g: true}, // IMPORTANT: Do not change this list without review from a DOM peer! - "MozPowerManager", + {name: "MozPowerManager", b2g: true, permission: "power"}, // IMPORTANT: Do not change this list without review from a DOM peer! "mozRTCIceCandidate", // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/webidl/Attr.webidl b/dom/webidl/Attr.webidl index d1fbaa8223..c836afd9b0 100644 --- a/dom/webidl/Attr.webidl +++ b/dom/webidl/Attr.webidl @@ -15,8 +15,11 @@ interface Attr : Node { [SetterThrows] attribute DOMString value; + [Constant] readonly attribute DOMString name; + [Constant] readonly attribute DOMString? namespaceURI; + [Constant] readonly attribute DOMString? prefix; readonly attribute boolean specified; diff --git a/dom/webidl/Crypto.webidl b/dom/webidl/Crypto.webidl index 75c5fcfbc4..31574a219c 100644 --- a/dom/webidl/Crypto.webidl +++ b/dom/webidl/Crypto.webidl @@ -7,12 +7,12 @@ * https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#crypto-interface */ -[NoInterfaceObject] +[NoInterfaceObject, Exposed=(Window,Worker)] interface GlobalCrypto { [Throws] readonly attribute Crypto crypto; }; -//[Exposed=(Window,Worker)] +[Exposed=(Window,Worker)] interface Crypto { readonly attribute SubtleCrypto subtle; diff --git a/dom/webidl/DecoderDoctorNotification.webidl b/dom/webidl/DecoderDoctorNotification.webidl new file mode 100644 index 0000000000..6f7ec2c6ff --- /dev/null +++ b/dom/webidl/DecoderDoctorNotification.webidl @@ -0,0 +1,15 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +enum DecoderDoctorNotificationType { + "cannot-play", + "can-play-but-some-missing-decoders" +}; + +dictionary DecoderDoctorNotification { + required DecoderDoctorNotificationType type; + DOMString formats; +}; diff --git a/dom/webidl/Element.webidl b/dom/webidl/Element.webidl index 7ee627dc05..116c5b3838 100644 --- a/dom/webidl/Element.webidl +++ b/dom/webidl/Element.webidl @@ -14,14 +14,13 @@ */ interface Element : Node { -/* - We haven't moved these from Node to Element like the spec wants. - - [Throws] + [Constant] readonly attribute DOMString? namespaceURI; + [Constant] readonly attribute DOMString? prefix; + [Constant] readonly attribute DOMString localName; -*/ + // Not [Constant] because it depends on which document we're in [Pure] readonly attribute DOMString tagName; diff --git a/dom/webidl/MediaKeyStatusMap.webidl b/dom/webidl/MediaKeyStatusMap.webidl index a4121f58df..adde37743b 100644 --- a/dom/webidl/MediaKeyStatusMap.webidl +++ b/dom/webidl/MediaKeyStatusMap.webidl @@ -13,8 +13,10 @@ enum MediaKeyStatus { "usable", "expired", + "released", + "output-restricted", "output-downscaled", - "output-not-allowed", + "status-pending", "internal-error" }; diff --git a/dom/webidl/MozPowerManager.webidl b/dom/webidl/MozPowerManager.webidl index 8aac2f2d47..a2250bc1c9 100644 --- a/dom/webidl/MozPowerManager.webidl +++ b/dom/webidl/MozPowerManager.webidl @@ -21,6 +21,7 @@ enum FactoryResetReason { /** * This interface implements navigator.mozPower */ +[CheckAnyPermissions="power"] interface MozPowerManager { [Throws] diff --git a/dom/webidl/Navigator.webidl b/dom/webidl/Navigator.webidl index 2bfac255f1..162cbd20f1 100644 --- a/dom/webidl/Navigator.webidl +++ b/dom/webidl/Navigator.webidl @@ -384,7 +384,6 @@ partial interface Navigator { }; #endif // MOZ_AUDIO_CHANNEL_MANAGER -#ifdef MOZ_MEDIA_NAVIGATOR callback NavigatorUserMediaSuccessCallback = void (MediaStream stream); callback NavigatorUserMediaErrorCallback = void (MediaStreamError error); @@ -418,7 +417,6 @@ partial interface Navigator { // now that devices are enumerated earlier. optional DOMString callID = ""); }; -#endif // MOZ_MEDIA_NAVIGATOR // Service Workers/Navigation Controllers partial interface Navigator { diff --git a/dom/webidl/Node.webidl b/dom/webidl/Node.webidl index 5400fff21f..9b9063fe2b 100644 --- a/dom/webidl/Node.webidl +++ b/dom/webidl/Node.webidl @@ -37,6 +37,8 @@ interface Node : EventTarget { [Pure] readonly attribute Document? ownerDocument; [Pure] + readonly attribute Node rootNode; + [Pure] readonly attribute Node? parentNode; [Pure] readonly attribute Element? parentElement; @@ -93,16 +95,6 @@ interface Node : EventTarget { boolean isDefaultNamespace(DOMString? namespace); // Mozilla-specific stuff - // These have been moved to Element in the spec. - // If we move namespaceURI, prefix and localName to Element they should return - // a non-nullable type. - [Constant] - readonly attribute DOMString? namespaceURI; - [Constant] - readonly attribute DOMString? prefix; - [Constant] - readonly attribute DOMString? localName; - [Throws, Func="IsChromeOrXBL"] any setUserData(DOMString key, any data); [Throws, Func="IsChromeOrXBL"] diff --git a/dom/webidl/NotifyPaintEvent.webidl b/dom/webidl/NotifyPaintEvent.webidl index 4de84951a2..799abbb299 100644 --- a/dom/webidl/NotifyPaintEvent.webidl +++ b/dom/webidl/NotifyPaintEvent.webidl @@ -13,4 +13,6 @@ interface NotifyPaintEvent : Event readonly attribute DOMRect boundingClientRect; readonly attribute PaintRequestList paintRequests; + + readonly attribute unsigned long long transactionId; }; diff --git a/dom/webidl/SubtleCrypto.webidl b/dom/webidl/SubtleCrypto.webidl index ebc4e84a41..328447ee7b 100644 --- a/dom/webidl/SubtleCrypto.webidl +++ b/dom/webidl/SubtleCrypto.webidl @@ -167,6 +167,7 @@ dictionary CryptoKeyPair { typedef DOMString KeyFormat; typedef (object or DOMString) AlgorithmIdentifier; +[Exposed=(Window,Worker)] interface SubtleCrypto { [Throws] Promise encrypt(AlgorithmIdentifier algorithm, diff --git a/dom/webidl/WorkerGlobalScope.webidl b/dom/webidl/WorkerGlobalScope.webidl index eaa88deadf..dea2632035 100644 --- a/dom/webidl/WorkerGlobalScope.webidl +++ b/dom/webidl/WorkerGlobalScope.webidl @@ -47,6 +47,7 @@ readonly attribute CacheStorage caches; WorkerGlobalScope implements WindowTimers; WorkerGlobalScope implements WindowBase64; WorkerGlobalScope implements GlobalFetch; +WorkerGlobalScope implements GlobalCrypto; WorkerGlobalScope implements IDBEnvironment; WorkerGlobalScope implements ImageBitmapFactories; diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index bc8af5c6c3..f97efc9b82 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -35,6 +35,7 @@ #include #endif +#include "Crypto.h" #include "Principal.h" #include "RuntimeService.h" #include "ScriptLoader.h" @@ -76,6 +77,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerGlobalScope, DOMEventTargetHelper) tmp->mWorkerPrivate->AssertIsOnWorkerThread(); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator) @@ -88,6 +90,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerGlobalScope, DOMEventTargetHelper) tmp->mWorkerPrivate->AssertIsOnWorkerThread(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation) NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator) @@ -132,6 +135,19 @@ WorkerGlobalScope::GetConsole(ErrorResult& aRv) return mConsole; } +Crypto* +WorkerGlobalScope::GetCrypto(ErrorResult& aError) +{ + mWorkerPrivate->AssertIsOnWorkerThread(); + + if (!mCrypto) { + mCrypto = new Crypto(); + mCrypto->Init(this); + } + + return mCrypto; +} + already_AddRefed WorkerGlobalScope::GetCaches(ErrorResult& aRv) { diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index ba11eea7ab..d9d59fc3d7 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -19,6 +19,7 @@ namespace dom { class AnyCallback; class Console; +class Crypto; class Function; class IDBFactory; class Promise; @@ -48,6 +49,7 @@ class WorkerGlobalScope : public DOMEventTargetHelper, typedef mozilla::dom::IDBFactory IDBFactory; RefPtr mConsole; + RefPtr mCrypto; RefPtr mLocation; RefPtr mNavigator; RefPtr mPerformance; @@ -94,6 +96,9 @@ public: return mConsole; } + Crypto* + GetCrypto(ErrorResult& aError); + already_AddRefed Location(); diff --git a/gfx/angle/BUILD.gn b/gfx/angle/BUILD.gn index 39bee5abaa..6c8a685154 100644 --- a/gfx/angle/BUILD.gn +++ b/gfx/angle/BUILD.gn @@ -15,24 +15,20 @@ angle_git_is_present = exec_script("src/commit_id.py", angle_use_commit_id = angle_git_is_present == 1 -gles_gypi = exec_script( - "//build/gypi_to_gn.py", - [ rebase_path("src/libGLESv2.gypi") ], - "scope", - [ "src/libGLESv2.gypi" ]) +gles_gypi = exec_script("//build/gypi_to_gn.py", + [ rebase_path("src/libGLESv2.gypi") ], + "scope", + [ "src/libGLESv2.gypi" ]) -compiler_gypi = exec_script( - "//build/gypi_to_gn.py", - [ rebase_path("src/compiler.gypi") ], - "scope", - [ "src/compiler.gypi" ]) +compiler_gypi = exec_script("//build/gypi_to_gn.py", + [ rebase_path("src/compiler.gypi") ], + "scope", + [ "src/compiler.gypi" ]) # This config is exported to dependent targets (and also applied to internal # ones). config("external_config") { - include_dirs = [ - "include", - ] + include_dirs = [ "include" ] } # This config is applied to internal Angle targets (not pushed to dependents). @@ -45,8 +41,12 @@ config("internal_config") { if (is_win) { copy("copy_compiler_dll") { - sources = [ "$windows_sdk_path/Redist/D3D/$target_cpu/d3dcompiler_47.dll" ] - outputs = [ "$root_build_dir/d3dcompiler_47.dll" ] + sources = [ + "$windows_sdk_path/Redist/D3D/$target_cpu/d3dcompiler_47.dll", + ] + outputs = [ + "$root_out_dir/d3dcompiler_47.dll", + ] } } @@ -58,11 +58,6 @@ component("translator") { defines = [ "ANGLE_TRANSLATOR_IMPLEMENTATION" ] - if (angle_enable_hlsl) { - sources += rebase_path(compiler_gypi.angle_translator_lib_hlsl_sources, ".", "src") - defines += [ "ANGLE_ENABLE_HLSL" ] - } - configs -= [ "//build/config/compiler:chromium_code" ] configs += [ ":internal_config", @@ -84,9 +79,9 @@ source_set("includes") { "include/GLES2/gl2ext.h", "include/GLES2/gl2platform.h", "include/GLES3/gl3.h", - "include/GLES3/gl3platform.h", "include/GLES3/gl31.h", "include/GLES3/gl32.h", + "include/GLES3/gl3platform.h", "include/GLSLANG/ShaderLang.h", "include/KHR/khrplatform.h", ] @@ -106,12 +101,9 @@ config("translator_static_config") { defines = [ "ANGLE_TRANSLATOR_STATIC" ] } - config("debug_annotations_config") { if (is_debug) { - defines = [ - "ANGLE_ENABLE_DEBUG_ANNOTATIONS", - ] + defines = [ "ANGLE_ENABLE_DEBUG_ANNOTATIONS" ] } } @@ -128,10 +120,24 @@ static_library("angle_common") { static_library("translator_lib") { sources = rebase_path(compiler_gypi.angle_translator_lib_sources, ".", "src") + defines = [] + + if (angle_enable_essl) { + sources += + rebase_path(compiler_gypi.angle_translator_lib_essl_sources, ".", "src") + defines += [ "ANGLE_ENABLE_ESSL" ] + } + + if (angle_enable_glsl) { + sources += + rebase_path(compiler_gypi.angle_translator_lib_glsl_sources, ".", "src") + defines += [ "ANGLE_ENABLE_GLSL" ] + } if (angle_enable_hlsl) { - sources += rebase_path(compiler_gypi.angle_translator_lib_hlsl_sources, ".", "src") - defines = [ "ANGLE_ENABLE_HLSL" ] + sources += + rebase_path(compiler_gypi.angle_translator_lib_hlsl_sources, ".", "src") + defines += [ "ANGLE_ENABLE_HLSL" ] } configs -= [ "//build/config/compiler:chromium_code" ] configs += [ @@ -222,6 +228,7 @@ config("libANGLE_config") { } defines += [ "GL_GLEXT_PROTOTYPES", + "EGL_EGLEXT_PROTOTYPES", ] if (is_win) { @@ -245,18 +252,13 @@ static_library("libANGLE") { include_dirs = [] libs = [] - defines = [ - "LIBANGLE_IMPLEMENTATION", - ] + defines = [ "LIBANGLE_IMPLEMENTATION" ] # Shared D3D sources. if (angle_enable_d3d9 || angle_enable_d3d11) { sources += rebase_path(gles_gypi.libangle_d3d_shared_sources, ".", "src") - defines += [ - "ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES={ " + - "\"d3dcompiler_47.dll\", \"d3dcompiler_46.dll\", \"d3dcompiler_43.dll\" }", - ] + defines += [ "ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES={ " + "\"d3dcompiler_47.dll\", \"d3dcompiler_46.dll\", \"d3dcompiler_43.dll\" }" ] } if (angle_enable_d3d9) { @@ -284,9 +286,7 @@ static_library("libANGLE") { } if (is_debug) { - defines += [ - "ANGLE_GENERATE_SHADER_DEBUG_INFO", - ] + defines += [ "ANGLE_GENERATE_SHADER_DEBUG_INFO" ] } configs -= [ "//build/config/compiler:chromium_code" ] @@ -299,10 +299,10 @@ static_library("libANGLE") { ] deps = [ + ":angle_common", ":commit_id", ":includes", ":translator_static", - ":angle_common", ] if (is_win) { @@ -314,8 +314,8 @@ shared_library("libGLESv2") { sources = rebase_path(gles_gypi.libglesv2_sources, ".", "src") if (is_win) { - ldflags = [ "/DEF:" + - rebase_path("src/libGLESv2/libGLESv2.def", root_build_dir) ] + ldflags = + [ "/DEF:" + rebase_path("src/libGLESv2/libGLESv2.def", root_build_dir) ] } configs -= [ "//build/config/compiler:chromium_code" ] @@ -327,9 +327,7 @@ shared_library("libGLESv2") { "//build/config/compiler:no_chromium_code", ] - defines = [ - "LIBGLESV2_IMPLEMENTATION", - ] + defines = [ "LIBGLESV2_IMPLEMENTATION" ] deps = [ ":includes", @@ -341,8 +339,7 @@ shared_library("libEGL") { sources = rebase_path(gles_gypi.libegl_sources, ".", "src") if (is_win) { - ldflags = [ "/DEF:" + - rebase_path("src/libEGL/libEGL.def", root_build_dir) ] + ldflags = [ "/DEF:" + rebase_path("src/libEGL/libEGL.def", root_build_dir) ] } configs -= [ "//build/config/compiler:chromium_code" ] @@ -354,9 +351,7 @@ shared_library("libEGL") { "//build/config/compiler:no_chromium_code", ] - defines = [ - "LIBEGL_IMPLEMENTATION", - ] + defines = [ "LIBEGL_IMPLEMENTATION" ] deps = [ ":includes", @@ -364,11 +359,17 @@ shared_library("libEGL") { ] } -util_gypi = exec_script( - "//build/gypi_to_gn.py", - [ rebase_path("util/util.gyp") ], - "scope", - [ "util/util.gyp" ]) +util_gypi = exec_script("//build/gypi_to_gn.py", + [ rebase_path("util/util.gyp") ], + "scope", + [ "util/util.gyp" ]) + +config("angle_util_config") { + include_dirs = [ "util" ] + if (is_linux) { + libs = [ "X11" ] + } +} static_library("angle_util") { sources = rebase_path(util_gypi.util_sources, ".", "util") @@ -395,16 +396,15 @@ static_library("angle_util") { "EGL_EGLEXT_PROTOTYPES", ] - configs += [ - ":internal_config", - ":debug_annotations_config", - ] + configs += [ ":debug_annotations_config" ] - include_dirs = [ - "util", + public_configs = [ + ":angle_util_config", + ":internal_config", ] deps = [ + ":angle_common", ":libEGL", ":libGLESv2", ] diff --git a/gfx/angle/DEPS b/gfx/angle/DEPS index d2cd173cad..056d764f8d 100644 --- a/gfx/angle/DEPS +++ b/gfx/angle/DEPS @@ -19,7 +19,7 @@ deps = { "https://android.googlesource.com/platform/external/cherry@af6c09fe05115f0cca61ae23ee871bda27cf1ff5", "third_party/deqp/src": - "https://android.googlesource.com/platform/external/deqp@92f7752da82925ca5e7288c5b4814efa7a381d89", + "https://android.googlesource.com/platform/external/deqp@cc0ded6c77267bbb14d21aac358fc5d9690c07f8", "third_party/libpng": "https://android.googlesource.com/platform/external/libpng@094e181e79a3d6c23fd005679025058b7df1ad6c", @@ -66,6 +66,40 @@ hooks = [ '-s', 'buildtools/linux64/clang-format.sha1', ], }, + # Pull GN binaries using checked-in hashes. + { + 'name': 'gn_win', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=win32', + '--no_auth', + '--bucket', 'chromium-gn', + '-s', 'buildtools/win/gn.exe.sha1', + ], + }, + { + 'name': 'gn_mac', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=darwin', + '--no_auth', + '--bucket', 'chromium-gn', + '-s', 'buildtools/mac/gn.sha1', + ], + }, + { + 'name': 'gn_linux64', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=linux*', + '--no_auth', + '--bucket', 'chromium-gn', + '-s', 'buildtools/linux64/gn.sha1', + ], + }, { # A change to a .gyp, .gypi, or to GYP itself should run the generator. "pattern": ".", diff --git a/gfx/angle/README.md b/gfx/angle/README.md index d2c28c5ef5..632b416348 100644 --- a/gfx/angle/README.md +++ b/gfx/angle/README.md @@ -7,6 +7,7 @@ The goal of ANGLE is to allow users of multiple operating systems to seamlessly | OpenGL ES 3.0 | | nearing completion | nearing completion | planned | [Level of OpenGL ES support via backing renderers] + | | Direct3D 9 | Direct3D 11 | Desktop GL | |------------:|:--------------:|:--------------:|:-------------:| | Windows | * | * | * | @@ -20,8 +21,11 @@ ANGLE is used as the default WebGL backend for both Google Chrome and Mozilla Fi Portions of the ANGLE shader compiler are used as a shader validator and translator by WebGL implementations across multiple platforms. It is used on Mac OS X, Linux, and in mobile variants of the browsers. Having one shader validator helps to ensure that a consistent set of GLSL ES shaders are accepted across browsers and platforms. The shader translator can be used to translate shaders to other shading languages, and to optionally apply shader modifications to work around bugs or quirks in the native graphics drivers. The translator targets Desktop GLSL, Direct3D HLSL, and even ESSL for native GLES2 platforms. +##Browsing +Browse ANGLE's source in the [repository](https://chromium.googlesource.com/angle/angle) + ##Building -View the [Dev setup instructions](doc/DevSetup.md). +View the [Dev setup instructions](doc/DevSetup.md). For generating a Windows Store version of ANGLE view the [Windows Store instructions](doc/BuildingAngleForWindowsStore.md) ##Contributing * Join our [Google group](https://groups.google.com/group/angleproject) to keep up to date. @@ -33,7 +37,8 @@ View the [Dev setup instructions](doc/DevSetup.md). * [Choose an ANGLE branch](doc/ChoosingANGLEBranch.md) to track in your own project. * File bugs in the [issue tracker](http://code.google.com/p/angleproject/issues/list) (preferably with an isolated test-case). * Read about WebGL on the [Khronos WebGL Wiki](http://khronos.org/webgl/wiki/Main_Page). -* Learn about implementation details in the [OpenGL Insights chapter on ANGLE](http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-ANGLE.pdf) and this [ANGLE presentation](https://code.google.com/p/angleproject/downloads/detail?name=ANGLE%20and%20Cross-Platform%20WebGL%20Support.pdf&can=2&q=). +* Learn about implementation details in the [OpenGL Insights chapter on ANGLE](http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-ANGLE.pdf) and this [ANGLE presentation](https://drive.google.com/file/d/0Bw29oYeC09QbbHoxNE5EUFh0RGs/view?usp=sharing). * Learn about the past, present, and future of the ANGLE implementation in [this recent presentation](https://docs.google.com/presentation/d/1CucIsdGVDmdTWRUbg68IxLE5jXwCb2y1E9YVhQo0thg/pub?start=false&loop=false). +* Notes on [debugging ANGLE](doc/DebuggingTips.md). * If you use ANGLE in your own project, we'd love to hear about it! diff --git a/gfx/angle/include/EGL/eglext.h b/gfx/angle/include/EGL/eglext.h index 199ba976e2..83490b8567 100644 --- a/gfx/angle/include/EGL/eglext.h +++ b/gfx/angle/include/EGL/eglext.h @@ -468,6 +468,11 @@ EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurfacePointerANGLE (EGLDisplay dpy, EGLSu #define EGL_ANGLE_surface_d3d_texture_2d_share_handle 1 #endif /* EGL_ANGLE_surface_d3d_texture_2d_share_handle */ +#ifndef EGL_ANGLE_direct_composition +#define EGL_ANGLE_direct_composition 1 +#define EGL_DIRECT_COMPOSITION_ANGLE 0x33A5 +#endif /* EGL_ANGLE_direct_composition */ + #ifndef EGL_ANGLE_platform_angle #define EGL_ANGLE_platform_angle 1 #define EGL_PLATFORM_ANGLE_ANGLE 0x3202 @@ -499,6 +504,31 @@ EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurfacePointerANGLE (EGLDisplay dpy, EGLSu #define EGL_FIXED_SIZE_ANGLE 0x3201 #endif /* EGL_ANGLE_window_fixed_size */ +#ifndef EGL_ANGLE_x11_visual +#define EGL_ANGLE_x11_visual +#define EGL_X11_VISUAL_ID_ANGLE 0x33A3 +#endif /* EGL_ANGLE_x11_visual */ + +#ifndef EGL_ANGLE_flexible_surface_compatibility +#define EGL_ANGLE_flexible_surface_compatibility 1 +#define EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE 0x33A6 +#endif /* EGL_ANGLE_flexible_surface_compatibility */ + +#ifndef EGL_ANGLE_surface_orientation +#define EGL_ANGLE_surface_orientation +#define EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE 0x33A7 +#define EGL_SURFACE_ORIENTATION_ANGLE 0x33A8 +#define EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE 0x0001 +#define EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE 0x0002 +#endif /* EGL_ANGLE_surface_orientation */ + +#ifndef EGL_ANGLE_experimental_present_path +#define EGL_ANGLE_experimental_present_path +#define EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE 0x33A4 +#define EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE 0x33A9 +#define EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE 0x33AA +#endif /* EGL_ANGLE_experimental_present_path */ + #ifndef EGL_ARM_pixmap_multisample_discard #define EGL_ARM_pixmap_multisample_discard 1 #define EGL_DISCARD_SAMPLES_ARM 0x3286 @@ -539,6 +569,16 @@ EGLAPI EGLBoolean EGLAPIENTRY eglQueryDisplayAttribEXT (EGLDisplay dpy, EGLint a #endif #endif /* EGL_EXT_device_base */ +#ifndef EGL_ANGLE_device_creation +#define EGL_ANGLE_device_creation 1 +typedef EGLDeviceEXT (EGLAPIENTRYP PFNEGLCREATEDEVICEANGLEPROC) (EGLint device_type, void *native_device, const EGLAttrib *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLRELEASEDEVICEANGLEPROC) (EGLDeviceEXT device); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLDeviceEXT EGLAPIENTRY eglCreateDeviceANGLE (EGLint device_type, void *native_device, const EGLAttrib *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglReleaseDeviceANGLE (EGLDeviceEXT device); +#endif +#endif /* EGL_ANGLE_device_creation */ + #ifndef EGL_EXT_device_drm #define EGL_EXT_device_drm 1 #define EGL_DRM_DEVICE_FILE_EXT 0x3233 diff --git a/gfx/angle/include/GLES2/gl2ext.h b/gfx/angle/include/GLES2/gl2ext.h index a878bcff6e..51886a2dcb 100644 --- a/gfx/angle/include/GLES2/gl2ext.h +++ b/gfx/angle/include/GLES2/gl2ext.h @@ -2920,6 +2920,21 @@ GL_APICALL void GL_APIENTRY glEndTilingQCOM (GLbitfield preserveMask); #define GL_SHADER_BINARY_VIV 0x8FC4 #endif /* GL_VIV_shader_binary */ +#ifndef GL_ANGLE_lossy_etc_decode +#define GL_ANGLE_lossy_etc_decode 1 +#define GL_ETC1_RGB8_LOSSY_DECODE_ANGLE 0x9690 +#define GL_COMPRESSED_R11_LOSSY_DECODE_EAC_ANGLE 0x9691 +#define GL_COMPRESSED_SIGNED_R11_LOSSY_DECODE_EAC_ANGLE 0x9692 +#define GL_COMPRESSED_RG11_LOSSY_DECODE_EAC_ANGLE 0x9693 +#define GL_COMPRESSED_SIGNED_RG11_LOSSY_DECODE_EAC_ANGLE 0x9694 +#define GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE 0x9695 +#define GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE 0x9696 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE 0x9697 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE 0x9698 +#define GL_COMPRESSED_RGBA8_LOSSY_DECODE_ETC2_EAC_ANGLE 0x9699 +#define GL_COMPRESSED_SRGB8_ALPHA8_LOSSY_DECODE_ETC2_EAC_ANGLE 0x969A +#endif /* GL_ANGLE_lossy_etc_decode */ + #ifdef __cplusplus } #endif diff --git a/gfx/angle/include/GLSLANG/ShaderLang.h b/gfx/angle/include/GLSLANG/ShaderLang.h index 1534f72f7e..d02723e760 100644 --- a/gfx/angle/include/GLSLANG/ShaderLang.h +++ b/gfx/angle/include/GLSLANG/ShaderLang.h @@ -48,7 +48,7 @@ typedef unsigned int GLenum; // Version number for shader translation API. // It is incremented every time the API changes. -#define ANGLE_SH_VERSION 140 +#define ANGLE_SH_VERSION 143 typedef enum { SH_GLES2_SPEC = 0x8B40, @@ -80,30 +80,35 @@ typedef enum { SH_CSS_SHADERS_SPEC = 0x8B42 } ShShaderSpec; -typedef enum { - SH_ESSL_OUTPUT = 0x8B45, - // SH_GLSL_OUTPUT is deprecated. This is to not break the build. - SH_GLSL_OUTPUT = 0x8B46, - SH_GLSL_COMPATIBILITY_OUTPUT = 0x8B46, - // SH_GLSL_CORE_OUTPUT is deprecated. - SH_GLSL_CORE_OUTPUT = 0x8B47, - //Note: GL introduced core profiles in 1.5. However, for compatiblity with Chromium, we treat SH_GLSL_CORE_OUTPUT as GLSL_130_OUTPUT. - //TODO: Remove SH_GLSL_CORE_OUTPUT - SH_GLSL_130_OUTPUT = 0x8B47, - SH_GLSL_140_OUTPUT = 0x8B80, - SH_GLSL_150_CORE_OUTPUT = 0x8B81, - SH_GLSL_330_CORE_OUTPUT = 0x8B82, - SH_GLSL_400_CORE_OUTPUT = 0x8B83, - SH_GLSL_410_CORE_OUTPUT = 0x8B84, - SH_GLSL_420_CORE_OUTPUT = 0x8B85, - SH_GLSL_430_CORE_OUTPUT = 0x8B86, - SH_GLSL_440_CORE_OUTPUT = 0x8B87, - SH_GLSL_450_CORE_OUTPUT = 0x8B88, +typedef enum +{ + // ESSL output only supported in some configurations. + SH_ESSL_OUTPUT = 0x8B45, - // HLSL output only supported in some configurations. - SH_HLSL_OUTPUT = 0x8B48, - SH_HLSL9_OUTPUT = 0x8B48, - SH_HLSL11_OUTPUT = 0x8B49 + // GLSL output only supported in some configurations. + SH_GLSL_COMPATIBILITY_OUTPUT = 0x8B46, + // Note: GL introduced core profiles in 1.5. + SH_GLSL_130_OUTPUT = 0x8B47, + SH_GLSL_140_OUTPUT = 0x8B80, + SH_GLSL_150_CORE_OUTPUT = 0x8B81, + SH_GLSL_330_CORE_OUTPUT = 0x8B82, + SH_GLSL_400_CORE_OUTPUT = 0x8B83, + SH_GLSL_410_CORE_OUTPUT = 0x8B84, + SH_GLSL_420_CORE_OUTPUT = 0x8B85, + SH_GLSL_430_CORE_OUTPUT = 0x8B86, + SH_GLSL_440_CORE_OUTPUT = 0x8B87, + SH_GLSL_450_CORE_OUTPUT = 0x8B88, + + // HLSL output only supported in some configurations. + // Deprecated: + SH_HLSL_OUTPUT = 0x8B48, + SH_HLSL9_OUTPUT = 0x8B48, + SH_HLSL11_OUTPUT = 0x8B49, + + // Prefer using these to specify HLSL output type: + SH_HLSL_3_0_OUTPUT = 0x8B48, // D3D 9 + SH_HLSL_4_1_OUTPUT = 0x8B49, // D3D 11 + SH_HLSL_4_0_FL9_3_OUTPUT = 0x8B4A // D3D 11 feature level 9_3 } ShShaderOutput; // Compile options. @@ -334,9 +339,9 @@ COMPILER_EXPORT const std::string &ShGetBuiltInResourcesString(const ShHandle ha // type: Specifies the type of shader - GL_FRAGMENT_SHADER or GL_VERTEX_SHADER. // spec: Specifies the language spec the compiler must conform to - // SH_GLES2_SPEC or SH_WEBGL_SPEC. -// output: Specifies the output code type - SH_ESSL_OUTPUT, SH_GLSL_OUTPUT, -// SH_HLSL9_OUTPUT or SH_HLSL11_OUTPUT. Note: HLSL output is only -// supported in some configurations. +// output: Specifies the output code type - for example SH_ESSL_OUTPUT, SH_GLSL_OUTPUT, +// SH_HLSL_3_0_OUTPUT or SH_HLSL_4_1_OUTPUT. Note: Each output type may only +// be supported in some configurations. // resources: Specifies the built-in resources. COMPILER_EXPORT ShHandle ShConstructCompiler( sh::GLenum type, diff --git a/gfx/angle/include/GLSLANG/ShaderVars.h b/gfx/angle/include/GLSLANG/ShaderVars.h index 0e82e7e61f..af9b65b7f5 100644 --- a/gfx/angle/include/GLSLANG/ShaderVars.h +++ b/gfx/angle/include/GLSLANG/ShaderVars.h @@ -200,6 +200,9 @@ struct COMPILER_EXPORT InterfaceBlock InterfaceBlock(const InterfaceBlock &other); InterfaceBlock &operator=(const InterfaceBlock &other); + // Fields from blocks with non-empty instance names are prefixed with the block name. + std::string fieldPrefix() const; + std::string name; std::string mappedName; std::string instanceName; @@ -210,6 +213,6 @@ struct COMPILER_EXPORT InterfaceBlock std::vector fields; }; -} +} // namespace sh #endif // GLSLANG_SHADERVARS_H_ diff --git a/gfx/angle/moz.build b/gfx/angle/moz.build index bad5ab5764..5b7b0fc28c 100644 --- a/gfx/angle/moz.build +++ b/gfx/angle/moz.build @@ -108,7 +108,8 @@ SOURCES += [ 'src/compiler/translator/glslang_tab.cpp', ] -if CONFIG['GNU_CXX'] or CONFIG['CLANG_CL']: + +if CONFIG['GNU_CXX']: CXXFLAGS += [ '-Wno-attributes', '-Wno-shadow', @@ -116,16 +117,16 @@ if CONFIG['GNU_CXX'] or CONFIG['CLANG_CL']: '-Wno-unknown-pragmas', '-Wno-unreachable-code', ] -if CONFIG['GNU_CXX'] and not CONFIG['CLANG_CXX'] and not CONFIG['CLANG_CL']: - CXXFLAGS += [ - '-Wno-shadow-compatible-local', - '-Wno-shadow-local', - ] -if CONFIG['CLANG_CXX'] or CONFIG['CLANG_CL']: - CXXFLAGS += [ - '-Wno-inconsistent-missing-override', - '-Wno-unused-private-field', - ] + if CONFIG['CLANG_CXX']: + CXXFLAGS += [ + '-Wno-inconsistent-missing-override', + '-Wno-unused-private-field', + ] + else: + CXXFLAGS += [ + '-Wno-shadow-compatible-local', + '-Wno-shadow-local', + ] if CONFIG['MOZ_DIRECTX_SDK_PATH'] and not CONFIG['MOZ_HAS_WINSDK_WITH_D3D']: LOCAL_INCLUDES += ['%' + '%s/include/' % CONFIG['MOZ_DIRECTX_SDK_PATH']] @@ -157,6 +158,8 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': DIRS += [ 'src/libANGLE', 'src/libGLESv2', 'src/libEGL' ] DEFINES['ANGLE_ENABLE_HLSL'] = "1" +DEFINES['ANGLE_ENABLE_GLSL'] = "1" +DEFINES['ANGLE_ENABLE_ESSL'] = "1" DEFINES['ANGLE_ENABLE_KEYEDMUTEX'] = "1" EXPORTS.angle += [ 'include/GLSLANG/ShaderLang.h', 'include/GLSLANG/ShaderVars.h' ] diff --git a/gfx/angle/src/angle.gyp b/gfx/angle/src/angle.gyp index 12f7a124d6..4421acca16 100644 --- a/gfx/angle/src/angle.gyp +++ b/gfx/angle/src/angle.gyp @@ -16,6 +16,8 @@ 'angle_enable_d3d9%': 0, 'angle_enable_d3d11%': 0, 'angle_enable_gl%': 0, + 'angle_enable_essl%': 1, # Enable this for all configs by default + 'angle_enable_glsl%': 1, # Enable this for all configs by default 'angle_enable_hlsl%': 0, 'angle_link_glx%': 0, 'angle_gl_library_type%': 'shared_library', diff --git a/gfx/angle/src/commit.h b/gfx/angle/src/commit.h index a263b2c067..12ca80c7c5 100644 --- a/gfx/angle/src/commit.h +++ b/gfx/angle/src/commit.h @@ -1,3 +1,3 @@ -#define ANGLE_COMMIT_HASH "316930d51ea9" +#define ANGLE_COMMIT_HASH "f1101625dbbe" #define ANGLE_COMMIT_HASH_SIZE 12 -#define ANGLE_COMMIT_DATE "2015-12-03 16:34:05 -0500" +#define ANGLE_COMMIT_DATE "2016-02-24 21:04:03 -0500" diff --git a/gfx/angle/src/common/BitSetIterator_unittest.cpp b/gfx/angle/src/common/BitSetIterator_unittest.cpp index 10fd3bea3f..e965f2c567 100644 --- a/gfx/angle/src/common/BitSetIterator_unittest.cpp +++ b/gfx/angle/src/common/BitSetIterator_unittest.cpp @@ -55,7 +55,7 @@ TEST_F(BitSetIteratorTest, EmptySet) for (unsigned long bit : IterateBitSet(mStateBits)) { sawBit = true; - UNUSED_TRACE_VARIABLE(bit); + UNUSED_VARIABLE(bit); } EXPECT_FALSE(sawBit); } diff --git a/gfx/angle/src/common/Optional.h b/gfx/angle/src/common/Optional.h index a606f0ada2..256f38f329 100644 --- a/gfx/angle/src/common/Optional.h +++ b/gfx/angle/src/common/Optional.h @@ -42,6 +42,13 @@ struct Optional return *this; } + Optional &operator=(T &&value) + { + mValue = std::move(value); + mValid = true; + return *this; + } + void reset() { mValid = false; diff --git a/gfx/angle/src/common/angleutils.cpp b/gfx/angle/src/common/angleutils.cpp index af5eb6c447..7099c21730 100644 --- a/gfx/angle/src/common/angleutils.cpp +++ b/gfx/angle/src/common/angleutils.cpp @@ -8,8 +8,15 @@ #include "common/debug.h" #include + +#include #include +namespace angle +{ +const uintptr_t DirtyPointer = std::numeric_limits::max(); +} + size_t FormatStringIntoVector(const char *fmt, va_list vararg, std::vector& outBuffer) { // Attempt to just print to the current buffer diff --git a/gfx/angle/src/common/angleutils.h b/gfx/angle/src/common/angleutils.h index c50a78eb59..a0178fd414 100644 --- a/gfx/angle/src/common/angleutils.h +++ b/gfx/angle/src/common/angleutils.h @@ -33,6 +33,7 @@ class NonCopyable void operator=(const NonCopyable&) = delete; }; +extern const uintptr_t DirtyPointer; } template diff --git a/gfx/angle/src/common/debug.cpp b/gfx/angle/src/common/debug.cpp index 466eb6d86c..1fcc062908 100644 --- a/gfx/angle/src/common/debug.cpp +++ b/gfx/angle/src/common/debug.cpp @@ -53,7 +53,7 @@ void output(bool traceInDebugOnly, MessageType messageType, DebugTraceOutputType } std::string formattedMessage; - UNUSED_TRACE_VARIABLE(formattedMessage); + UNUSED_VARIABLE(formattedMessage); #if !defined(NDEBUG) && defined(_MSC_VER) if (messageType == MESSAGE_ERR) diff --git a/gfx/angle/src/common/debug.h b/gfx/angle/src/common/debug.h index 3047598674..64cfef4cd9 100644 --- a/gfx/angle/src/common/debug.h +++ b/gfx/angle/src/common/debug.h @@ -16,7 +16,7 @@ #include "common/angleutils.h" #if !defined(TRACE_OUTPUT_FILE) -#define TRACE_OUTPUT_FILE "debug.txt" +#define TRACE_OUTPUT_FILE "angle_debug.txt" #endif namespace gl @@ -91,7 +91,7 @@ bool DebugAnnotationsActive(); #if defined(_MSC_VER) #define EVENT(message, ...) gl::ScopedPerfEventHelper scopedPerfEventHelper ## __LINE__("%s" message "\n", __FUNCTION__, __VA_ARGS__); #else -#define EVENT(message, ...) gl::ScopedPerfEventHelper scopedPerfEventHelper(message "\n", ##__VA_ARGS__); +#define EVENT(message, ...) gl::ScopedPerfEventHelper scopedPerfEventHelper("%s" message "\n", __FUNCTION__, ##__VA_ARGS__); #endif // _MSC_VER #else #define EVENT(message, ...) (void(0)) @@ -105,7 +105,7 @@ bool DebugAnnotationsActive(); #if !defined(NDEBUG) #define ASSERT(expression) { \ if(!(expression)) \ - ERR("\t! Assert failed in %s(%d): "#expression"\n", __FUNCTION__, __LINE__); \ + ERR("\t! Assert failed in %s(%d): %s\n", __FUNCTION__, __LINE__, #expression); \ assert(expression); \ } ANGLE_EMPTY_STATEMENT #define UNUSED_ASSERTION_VARIABLE(variable) @@ -114,11 +114,7 @@ bool DebugAnnotationsActive(); #define UNUSED_ASSERTION_VARIABLE(variable) ((void)variable) #endif -#ifndef ANGLE_ENABLE_DEBUG_TRACE -#define UNUSED_TRACE_VARIABLE(variable) ((void)variable) -#else -#define UNUSED_TRACE_VARIABLE(variable) -#endif +#define UNUSED_VARIABLE(variable) ((void)variable) // A macro to indicate unimplemented functionality diff --git a/gfx/angle/src/common/mathutil.h b/gfx/angle/src/common/mathutil.h index 7aff8aa56d..3de62aef10 100644 --- a/gfx/angle/src/common/mathutil.h +++ b/gfx/angle/src/common/mathutil.h @@ -493,9 +493,10 @@ inline unsigned int average(unsigned int a, unsigned int b) return ((a ^ b) >> 1) + (a & b); } -inline signed int average(signed int a, signed int b) +inline int average(int a, int b) { - return ((long long)a + (long long)b) / 2; + long long average = (static_cast(a) + static_cast(b)) / 2ll; + return static_cast(average); } inline float average(float a, float b) @@ -584,9 +585,10 @@ struct IndexRange // packSnorm2x16 : round(clamp(c, -1, +1) * 32767.0) inline uint32_t packSnorm2x16(float f1, float f2) { - uint16_t leastSignificantBits = static_cast(roundf(clamp(f1, -1.0f, 1.0f) * 32767.0f)); - uint16_t mostSignificantBits = static_cast(roundf(clamp(f2, -1.0f, 1.0f) * 32767.0f)); - return static_cast(mostSignificantBits) << 16 | static_cast(leastSignificantBits); + int16_t leastSignificantBits = static_cast(roundf(clamp(f1, -1.0f, 1.0f) * 32767.0f)); + int16_t mostSignificantBits = static_cast(roundf(clamp(f2, -1.0f, 1.0f) * 32767.0f)); + return static_cast(mostSignificantBits) << 16 | + (static_cast(leastSignificantBits) & 0xFFFF); } // First, unpacks a single 32-bit unsigned integer u into a pair of 16-bit unsigned integers. Then, each diff --git a/gfx/angle/src/common/string_utils.cpp b/gfx/angle/src/common/string_utils.cpp index 0d3869cf84..acb0376bf9 100644 --- a/gfx/angle/src/common/string_utils.cpp +++ b/gfx/angle/src/common/string_utils.cpp @@ -15,26 +15,53 @@ namespace angle { -void SplitString(const std::string &input, - char delimiter, - std::vector *tokensOut) -{ - std::istringstream stream(input); - std::string token; +const char kWhitespaceASCII[] = " \f\n\r\t\v"; - while (std::getline(stream, token, delimiter)) +std::vector SplitString(const std::string &input, + const std::string &delimiters, + WhitespaceHandling whitespace, + SplitResult resultType) +{ + std::vector result; + if (input.empty()) { - if (!token.empty()) + return result; + } + + std::string::size_type start = 0; + while (start != std::string::npos) + { + auto end = input.find_first_of(delimiters, start); + + std::string piece; + if (end == std::string::npos) { - tokensOut->push_back(token); + piece = input.substr(start); + start = std::string::npos; + } + else + { + piece = input.substr(start, end - start); + start = end + 1; + } + + if (whitespace == TRIM_WHITESPACE) + { + piece = TrimString(piece, kWhitespaceASCII); + } + + if (resultType == SPLIT_WANT_ALL || !piece.empty()) + { + result.push_back(piece); } } + + return result; } void SplitStringAlongWhitespace(const std::string &input, std::vector *tokensOut) { - const char *delimiters = " \f\n\r\t\v"; std::istringstream stream(input); std::string line; @@ -42,7 +69,7 @@ void SplitStringAlongWhitespace(const std::string &input, while (std::getline(stream, line)) { size_t prev = 0, pos; - while ((pos = line.find_first_of(delimiters, prev)) != std::string::npos) + while ((pos = line.find_first_of(kWhitespaceASCII, prev)) != std::string::npos) { if (pos > prev) tokensOut->push_back(line.substr(prev, pos - prev)); @@ -53,6 +80,23 @@ void SplitStringAlongWhitespace(const std::string &input, } } +std::string TrimString(const std::string &input, const std::string &trimChars) +{ + auto begin = input.find_first_not_of(trimChars); + if (begin == std::string::npos) + { + return ""; + } + + std::string::size_type end = input.find_last_not_of(trimChars); + if (end == std::string::npos) + { + return input.substr(begin); + } + + return input.substr(begin, end - begin + 1); +} + bool HexStringToUInt(const std::string &input, unsigned int *uintOut) { unsigned int offset = 0; diff --git a/gfx/angle/src/common/string_utils.h b/gfx/angle/src/common/string_utils.h index 78d1ec018d..131b17e086 100644 --- a/gfx/angle/src/common/string_utils.h +++ b/gfx/angle/src/common/string_utils.h @@ -16,13 +16,30 @@ namespace angle { -void SplitString(const std::string &input, - char delimiter, - std::vector *tokensOut); +extern const char kWhitespaceASCII[]; + +enum WhitespaceHandling +{ + KEEP_WHITESPACE, + TRIM_WHITESPACE, +}; + +enum SplitResult +{ + SPLIT_WANT_ALL, + SPLIT_WANT_NONEMPTY, +}; + +std::vector SplitString(const std::string &input, + const std::string &delimiters, + WhitespaceHandling whitespace, + SplitResult resultType); void SplitStringAlongWhitespace(const std::string &input, std::vector *tokensOut); +std::string TrimString(const std::string &input, const std::string &trimChars); + bool HexStringToUInt(const std::string &input, unsigned int *uintOut); bool ReadFileToString(const std::string &path, std::string *stringOut); diff --git a/gfx/angle/src/common/string_utils_unittest.cpp b/gfx/angle/src/common/string_utils_unittest.cpp index 6d7a3ecc1a..1ab03d003a 100644 --- a/gfx/angle/src/common/string_utils_unittest.cpp +++ b/gfx/angle/src/common/string_utils_unittest.cpp @@ -16,34 +16,101 @@ using namespace angle; namespace { -// Basic functionality tests for SplitString -TEST(StringUtilsTest, SplitStringBasic) +// Basic SplitString tests +TEST(StringUtilsTest, SplitString_Basics) { - std::string testString("AxBxCxxxDExxFGHx"); - std::vector tokens; - SplitString(testString, 'x', &tokens); + std::vector r; - ASSERT_EQ(5u, tokens.size()); - EXPECT_EQ("A", tokens[0]); - EXPECT_EQ("B", tokens[1]); - EXPECT_EQ("C", tokens[2]); - EXPECT_EQ("DE", tokens[3]); - EXPECT_EQ("FGH", tokens[4]); + r = SplitString(std::string(), ",:;", KEEP_WHITESPACE, SPLIT_WANT_ALL); + EXPECT_TRUE(r.empty()); + + // Empty separator list + r = SplitString("hello, world", "", KEEP_WHITESPACE, SPLIT_WANT_ALL); + ASSERT_EQ(1u, r.size()); + EXPECT_EQ("hello, world", r[0]); + + // Should split on any of the separators. + r = SplitString("::,,;;", ",:;", KEEP_WHITESPACE, SPLIT_WANT_ALL); + ASSERT_EQ(7u, r.size()); + for (auto str : r) + ASSERT_TRUE(str.empty()); + + r = SplitString("red, green; blue:", ",:;", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); + ASSERT_EQ(3u, r.size()); + EXPECT_EQ("red", r[0]); + EXPECT_EQ("green", r[1]); + EXPECT_EQ("blue", r[2]); + + // Want to split a string along whitespace sequences. + r = SplitString(" red green \tblue\n", " \t\n", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); + ASSERT_EQ(3u, r.size()); + EXPECT_EQ("red", r[0]); + EXPECT_EQ("green", r[1]); + EXPECT_EQ("blue", r[2]); + + // Weird case of splitting on spaces but not trimming. + r = SplitString(" red ", " ", TRIM_WHITESPACE, SPLIT_WANT_ALL); + ASSERT_EQ(3u, r.size()); + EXPECT_EQ("", r[0]); // Before the first space. + EXPECT_EQ("red", r[1]); + EXPECT_EQ("", r[2]); // After the last space. } -// Basic functionality tests for SplitStringAlongWhitespace -TEST(StringUtilsTest, SplitStringAlongWhitespaceBasic) +// Check different whitespace and result types for SplitString +TEST(StringUtilsTest, SplitString_WhitespaceAndResultType) { - std::string testString("A B\nC\r\tDE\v\fFGH \t\r\n"); - std::vector tokens; - SplitStringAlongWhitespace(testString, &tokens); + std::vector r; - ASSERT_EQ(5u, tokens.size()); - EXPECT_EQ("A", tokens[0]); - EXPECT_EQ("B", tokens[1]); - EXPECT_EQ("C", tokens[2]); - EXPECT_EQ("DE", tokens[3]); - EXPECT_EQ("FGH", tokens[4]); + // Empty input handling. + r = SplitString(std::string(), ",", KEEP_WHITESPACE, SPLIT_WANT_ALL); + EXPECT_TRUE(r.empty()); + r = SplitString(std::string(), ",", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY); + EXPECT_TRUE(r.empty()); + + // Input string is space and we're trimming. + r = SplitString(" ", ",", TRIM_WHITESPACE, SPLIT_WANT_ALL); + ASSERT_EQ(1u, r.size()); + EXPECT_EQ("", r[0]); + r = SplitString(" ", ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); + EXPECT_TRUE(r.empty()); + + // Test all 4 combinations of flags on ", ,". + r = SplitString(", ,", ",", KEEP_WHITESPACE, SPLIT_WANT_ALL); + ASSERT_EQ(3u, r.size()); + EXPECT_EQ("", r[0]); + EXPECT_EQ(" ", r[1]); + EXPECT_EQ("", r[2]); + r = SplitString(", ,", ",", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY); + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(" ", r[0]); + r = SplitString(", ,", ",", TRIM_WHITESPACE, SPLIT_WANT_ALL); + ASSERT_EQ(3u, r.size()); + EXPECT_EQ("", r[0]); + EXPECT_EQ("", r[1]); + EXPECT_EQ("", r[2]); + r = SplitString(", ,", ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); + ASSERT_TRUE(r.empty()); +} + +// Tests for TrimString +TEST(StringUtilsTest, TrimString) +{ + // Basic tests + EXPECT_EQ("a", TrimString("a", kWhitespaceASCII)); + EXPECT_EQ("a", TrimString(" a", kWhitespaceASCII)); + EXPECT_EQ("a", TrimString("a ", kWhitespaceASCII)); + EXPECT_EQ("a", TrimString(" a ", kWhitespaceASCII)); + + // Tests with empty strings + EXPECT_EQ("", TrimString("", kWhitespaceASCII)); + EXPECT_EQ("", TrimString(" \n\r\t", kWhitespaceASCII)); + EXPECT_EQ(" foo ", TrimString(" foo ", "")); + + // Tests it doesn't removes characters in the middle + EXPECT_EQ("foo bar", TrimString(" foo bar ", kWhitespaceASCII)); + + // Test with non-whitespace trimChars + EXPECT_EQ(" ", TrimString("foo bar", "abcdefghijklmnopqrstuvwxyz")); } // Basic functionality tests for HexStringToUInt diff --git a/gfx/angle/src/compiler.gypi b/gfx/angle/src/compiler.gypi index b2c590cfed..36a3597e19 100644 --- a/gfx/angle/src/compiler.gypi +++ b/gfx/angle/src/compiler.gypi @@ -25,8 +25,6 @@ 'compiler/translator/BaseTypes.h', 'compiler/translator/BuiltInFunctionEmulator.cpp', 'compiler/translator/BuiltInFunctionEmulator.h', - 'compiler/translator/BuiltInFunctionEmulatorGLSL.cpp', - 'compiler/translator/BuiltInFunctionEmulatorGLSL.h', 'compiler/translator/Cache.cpp', 'compiler/translator/Cache.h', 'compiler/translator/CallDAG.cpp', @@ -43,8 +41,6 @@ 'compiler/translator/EmulatePrecision.cpp', 'compiler/translator/EmulatePrecision.h', 'compiler/translator/ExtensionBehavior.h', - 'compiler/translator/ExtensionGLSL.cpp', - 'compiler/translator/ExtensionGLSL.h', 'compiler/translator/FlagStd140Structs.cpp', 'compiler/translator/FlagStd140Structs.h', 'compiler/translator/ForLoopUnroll.cpp', @@ -72,12 +68,6 @@ 'compiler/translator/NodeSearch.h', 'compiler/translator/Operator.cpp', 'compiler/translator/Operator.h', - 'compiler/translator/OutputESSL.cpp', - 'compiler/translator/OutputESSL.h', - 'compiler/translator/OutputGLSL.cpp', - 'compiler/translator/OutputGLSL.h', - 'compiler/translator/OutputGLSLBase.cpp', - 'compiler/translator/OutputGLSLBase.h', 'compiler/translator/ParseContext.cpp', 'compiler/translator/ParseContext.h', 'compiler/translator/PoolAlloc.cpp', @@ -100,10 +90,6 @@ 'compiler/translator/SearchSymbol.h', 'compiler/translator/SymbolTable.cpp', 'compiler/translator/SymbolTable.h', - 'compiler/translator/TranslatorESSL.cpp', - 'compiler/translator/TranslatorESSL.h', - 'compiler/translator/TranslatorGLSL.cpp', - 'compiler/translator/TranslatorGLSL.h', 'compiler/translator/Types.cpp', 'compiler/translator/Types.h', 'compiler/translator/UnfoldShortCircuitAST.cpp', @@ -120,8 +106,6 @@ 'compiler/translator/VariableInfo.h', 'compiler/translator/VariablePacker.cpp', 'compiler/translator/VariablePacker.h', - 'compiler/translator/VersionGLSL.cpp', - 'compiler/translator/VersionGLSL.h', 'compiler/translator/blocklayout.cpp', 'compiler/translator/blocklayout.h', 'compiler/translator/depgraph/DependencyGraph.cpp', @@ -148,6 +132,28 @@ 'third_party/compiler/ArrayBoundsClamper.cpp', 'third_party/compiler/ArrayBoundsClamper.h', ], + 'angle_translator_lib_essl_sources': + [ + 'compiler/translator/OutputESSL.cpp', + 'compiler/translator/OutputESSL.h', + 'compiler/translator/TranslatorESSL.cpp', + 'compiler/translator/TranslatorESSL.h', + ], + 'angle_translator_lib_glsl_sources': + [ + 'compiler/translator/BuiltInFunctionEmulatorGLSL.cpp', + 'compiler/translator/BuiltInFunctionEmulatorGLSL.h', + 'compiler/translator/ExtensionGLSL.cpp', + 'compiler/translator/ExtensionGLSL.h', + 'compiler/translator/OutputGLSL.cpp', + 'compiler/translator/OutputGLSL.h', + 'compiler/translator/OutputGLSLBase.cpp', + 'compiler/translator/OutputGLSLBase.h', + 'compiler/translator/TranslatorGLSL.cpp', + 'compiler/translator/TranslatorGLSL.h', + 'compiler/translator/VersionGLSL.cpp', + 'compiler/translator/VersionGLSL.h', + ], 'angle_translator_lib_hlsl_sources': [ 'compiler/translator/ArrayReturnValueToOutParameter.cpp', @@ -253,6 +259,42 @@ }, 'conditions': [ + ['angle_enable_essl==1', + { + 'defines': + [ + 'ANGLE_ENABLE_ESSL', + ], + 'direct_dependent_settings': + { + 'defines': + [ + 'ANGLE_ENABLE_ESSL', + ], + }, + 'sources': + [ + '<@(angle_translator_lib_essl_sources)', + ], + }], + ['angle_enable_glsl==1', + { + 'defines': + [ + 'ANGLE_ENABLE_GLSL', + ], + 'direct_dependent_settings': + { + 'defines': + [ + 'ANGLE_ENABLE_GLSL', + ], + }, + 'sources': + [ + '<@(angle_translator_lib_glsl_sources)', + ], + }], ['angle_enable_hlsl==1', { 'defines': diff --git a/gfx/angle/src/compiler/translator/ASTMetadataHLSL.cpp b/gfx/angle/src/compiler/translator/ASTMetadataHLSL.cpp index cc21a00054..31bfae9966 100644 --- a/gfx/angle/src/compiler/translator/ASTMetadataHLSL.cpp +++ b/gfx/angle/src/compiler/translator/ASTMetadataHLSL.cpp @@ -66,13 +66,13 @@ class PullGradient : public TIntermTraverser } } - bool visitLoop(Visit visit, TIntermLoop *loop) + bool visitLoop(Visit visit, TIntermLoop *loop) override { visitControlFlow(visit, loop); return true; } - bool visitSelection(Visit visit, TIntermSelection *selection) + bool visitSelection(Visit visit, TIntermSelection *selection) override { visitControlFlow(visit, selection); return true; @@ -333,7 +333,7 @@ class PushDiscontinuousLoops : public TIntermTraverser ASSERT(mNestedDiscont == (mMetadata->mCalledInDiscontinuousLoop ? 1 : 0)); } - bool visitLoop(Visit visit, TIntermLoop *loop) + bool visitLoop(Visit visit, TIntermLoop *loop) override { bool isDiscontinuous = mMetadata->mDiscontinuousLoops.count(loop) > 0; diff --git a/gfx/angle/src/compiler/translator/CallDAG.cpp b/gfx/angle/src/compiler/translator/CallDAG.cpp index eb6c031908..10f0eb937c 100644 --- a/gfx/angle/src/compiler/translator/CallDAG.cpp +++ b/gfx/angle/src/compiler/translator/CallDAG.cpp @@ -35,6 +35,7 @@ class CallDAG::CallDAGCreator : public TIntermTraverser InitResult result = assignIndicesInternal(&it.second); if (result != INITDAG_SUCCESS) { + *mCreationInfo << "\n"; return result; } } @@ -107,7 +108,8 @@ class CallDAG::CallDAGCreator : public TIntermTraverser if (visit == PreVisit) { // Function declaration, create an empty record. - mFunctions[node->getName()]; + auto& record = mFunctions[node->getName()]; + record.name = node->getName(); } break; case EOpFunction: @@ -169,7 +171,8 @@ class CallDAG::CallDAGCreator : public TIntermTraverser if (!function->node) { - *mCreationInfo << "Undefined function: " << function->name; + *mCreationInfo << "Undefined function '" << function->name + << ")' used in the following call chain:"; return INITDAG_UNDEFINED; } @@ -182,7 +185,7 @@ class CallDAG::CallDAGCreator : public TIntermTraverser { if (mCreationInfo) { - *mCreationInfo << "Recursive function call in the following call chain: " << function->name; + *mCreationInfo << "Recursive function call in the following call chain:" << function->name; } return INITDAG_RECURSION; } @@ -191,19 +194,15 @@ class CallDAG::CallDAGCreator : public TIntermTraverser for (auto &callee : function->callees) { InitResult result = assignIndicesInternal(callee); - if (result == INITDAG_RECURSION) + if (result != INITDAG_SUCCESS) { - // We know that there is a recursive function call chain in the AST, + // We know that there is an issue with the call chain in the AST, // print the link of the chain we were processing. if (mCreationInfo) { - *mCreationInfo << " <- " << function->name; + *mCreationInfo << " <- " << function->name << ")"; } - return INITDAG_RECURSION; - } - else if (result == INITDAG_UNDEFINED) - { - return INITDAG_UNDEFINED; + return result; } } diff --git a/gfx/angle/src/compiler/translator/CodeGen.cpp b/gfx/angle/src/compiler/translator/CodeGen.cpp index 4f8d2b085a..f099bccf15 100644 --- a/gfx/angle/src/compiler/translator/CodeGen.cpp +++ b/gfx/angle/src/compiler/translator/CodeGen.cpp @@ -4,8 +4,14 @@ // found in the LICENSE file. // +#ifdef ANGLE_ENABLE_ESSL #include "compiler/translator/TranslatorESSL.h" +#endif + +#ifdef ANGLE_ENABLE_GLSL #include "compiler/translator/TranslatorGLSL.h" +#endif + #ifdef ANGLE_ENABLE_HLSL #include "compiler/translator/TranslatorHLSL.h" #endif // ANGLE_ENABLE_HLSL @@ -20,7 +26,13 @@ TCompiler* ConstructCompiler( { switch (output) { case SH_ESSL_OUTPUT: +#ifdef ANGLE_ENABLE_ESSL return new TranslatorESSL(type, spec); +#else + // This compiler is not supported in this + // configuration. Return NULL per the ShConstructCompiler API. + return nullptr; +#endif // ANGLE_ENABLE_ESSL case SH_GLSL_130_OUTPUT: case SH_GLSL_140_OUTPUT: case SH_GLSL_150_CORE_OUTPUT: @@ -32,19 +44,26 @@ TCompiler* ConstructCompiler( case SH_GLSL_440_CORE_OUTPUT: case SH_GLSL_450_CORE_OUTPUT: case SH_GLSL_COMPATIBILITY_OUTPUT: +#ifdef ANGLE_ENABLE_GLSL return new TranslatorGLSL(type, spec, output); - case SH_HLSL9_OUTPUT: - case SH_HLSL11_OUTPUT: +#else + // This compiler is not supported in this + // configuration. Return NULL per the ShConstructCompiler API. + return nullptr; +#endif // ANGLE_ENABLE_GLSL + case SH_HLSL_3_0_OUTPUT: + case SH_HLSL_4_1_OUTPUT: + case SH_HLSL_4_0_FL9_3_OUTPUT: #ifdef ANGLE_ENABLE_HLSL return new TranslatorHLSL(type, spec, output); #else // This compiler is not supported in this // configuration. Return NULL per the ShConstructCompiler API. - return NULL; + return nullptr; #endif // ANGLE_ENABLE_HLSL default: // Unknown format. Return NULL per the ShConstructCompiler API. - return NULL; + return nullptr; } } diff --git a/gfx/angle/src/compiler/translator/Common.h b/gfx/angle/src/compiler/translator/Common.h index 5759302243..60223232af 100644 --- a/gfx/angle/src/compiler/translator/Common.h +++ b/gfx/angle/src/compiler/translator/Common.h @@ -60,18 +60,21 @@ inline TString* NewPoolTString(const char* s) // // Pool allocator versions of vectors, lists, and maps // -template class TVector : public std::vector > { -public: - typedef typename std::vector >::size_type size_type; - TVector() : std::vector >() {} - TVector(const pool_allocator& a) : std::vector >(a) {} - TVector(size_type i): std::vector >(i) {} +template +class TVector : public std::vector> +{ + public: + typedef typename std::vector>::size_type size_type; + TVector() : std::vector>() {} + TVector(const pool_allocator &a) : std::vector>(a) {} + TVector(size_type i) : std::vector>(i) {} }; -template > -class TMap : public std::map > > { -public: - typedef pool_allocator > tAllocator; +template > +class TMap : public std::map>> +{ + public: + typedef pool_allocator> tAllocator; TMap() : std::map() {} // use correct two-stage name lookup supported in gcc 3.4 and above diff --git a/gfx/angle/src/compiler/translator/Compiler.cpp b/gfx/angle/src/compiler/translator/Compiler.cpp index 9002ce65fa..18524ce569 100644 --- a/gfx/angle/src/compiler/translator/Compiler.cpp +++ b/gfx/angle/src/compiler/translator/Compiler.cpp @@ -294,12 +294,14 @@ TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[], // Unroll for-loop markup needs to happen after validateLimitations pass. if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX)) { - ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex); + ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex, + shouldRunLoopAndIndexingValidation(compileOptions)); root->traverse(&marker); } if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX)) { - ForLoopUnrollMarker marker(ForLoopUnrollMarker::kSamplerArrayIndex); + ForLoopUnrollMarker marker(ForLoopUnrollMarker::kSamplerArrayIndex, + shouldRunLoopAndIndexingValidation(compileOptions)); root->traverse(&marker); if (marker.samplerArrayIndexIsFloatLoopIndex()) { @@ -320,7 +322,10 @@ TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[], if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS)) arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root); - if (success && shaderType == GL_VERTEX_SHADER && (compileOptions & SH_INIT_GL_POSITION)) + // gl_Position is always written in compatibility output mode + if (success && shaderType == GL_VERTEX_SHADER && + ((compileOptions & SH_INIT_GL_POSITION) || + (outputType == SH_GLSL_COMPATIBILITY_OUTPUT))) initializeGLPosition(root); // This pass might emit short circuits so keep it before the short circuit unfolding @@ -423,11 +428,6 @@ bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources) floatingPoint.secondarySize = 1; floatingPoint.array = false; - TPublicType sampler; - sampler.primarySize = 1; - sampler.secondarySize = 1; - sampler.array = false; - switch(shaderType) { case GL_FRAGMENT_SHADER: @@ -440,14 +440,15 @@ bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources) default: assert(false && "Language not supported"); } - // We set defaults for all the sampler types, even those that are + // Set defaults for sampler types that have default precision, even those that are // only available if an extension exists. - for (int samplerType = EbtGuardSamplerBegin + 1; - samplerType < EbtGuardSamplerEnd; ++samplerType) - { - sampler.type = static_cast(samplerType); - symbolTable.setDefaultPrecision(sampler, EbpLow); - } + // New sampler types in ESSL3 don't have default precision. ESSL1 types do. + initSamplerDefaultPrecision(EbtSampler2D); + initSamplerDefaultPrecision(EbtSamplerCube); + // SamplerExternalOES is specified in the extension to have default precision. + initSamplerDefaultPrecision(EbtSamplerExternalOES); + // It isn't specified whether Sampler2DRect has default precision. + initSamplerDefaultPrecision(EbtSampler2DRect); InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable); @@ -456,6 +457,17 @@ bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources) return true; } +void TCompiler::initSamplerDefaultPrecision(TBasicType samplerType) +{ + ASSERT(samplerType > EbtGuardSamplerBegin && samplerType < EbtGuardSamplerEnd); + TPublicType sampler; + sampler.primarySize = 1; + sampler.secondarySize = 1; + sampler.array = false; + sampler.type = samplerType; + symbolTable.setDefaultPrecision(sampler, EbpLow); +} + void TCompiler::setResourceString() { std::ostringstream strstream; @@ -598,7 +610,7 @@ bool TCompiler::tagUsedFunctions() } infoSink.info.prefix(EPrefixError); - infoSink.info << "Missing main()"; + infoSink.info << "Missing main()\n"; return false; } @@ -689,7 +701,7 @@ void TCompiler::rewriteCSSShader(TIntermNode* root) bool TCompiler::validateLimitations(TIntermNode* root) { - ValidateLimitations validate(shaderType, infoSink.info); + ValidateLimitations validate(shaderType, &infoSink.info); root->traverse(&validate); return validate.numErrors() == 0; } @@ -735,17 +747,6 @@ bool TCompiler::limitExpressionComplexity(TIntermNode* root) return false; } - TDependencyGraph graph(root); - - for (TFunctionCallVector::const_iterator iter = graph.beginUserDefinedFunctionCalls(); - iter != graph.endUserDefinedFunctionCalls(); - ++iter) - { - TGraphFunctionCall* samplerSymbol = *iter; - TDependencyGraphTraverser graphTraverser; - samplerSymbol->traverse(&graphTraverser); - } - return true; } diff --git a/gfx/angle/src/compiler/translator/Compiler.h b/gfx/angle/src/compiler/translator/Compiler.h index 736d9ef242..c00a8f97aa 100644 --- a/gfx/angle/src/compiler/translator/Compiler.h +++ b/gfx/angle/src/compiler/translator/Compiler.h @@ -179,6 +179,8 @@ class TCompiler : public TShHandleBase bool tagUsedFunctions(); void internalTagUsedFunction(size_t index); + void initSamplerDefaultPrecision(TBasicType samplerType); + // Removes unused function declarations and prototypes from the AST class UnusedPredicate; bool pruneUnusedFunctions(TIntermNode *root); diff --git a/gfx/angle/src/compiler/translator/DirectiveHandler.cpp b/gfx/angle/src/compiler/translator/DirectiveHandler.cpp index 084c22d5ea..ff8a69efa5 100644 --- a/gfx/angle/src/compiler/translator/DirectiveHandler.cpp +++ b/gfx/angle/src/compiler/translator/DirectiveHandler.cpp @@ -8,6 +8,7 @@ #include +#include "angle_gl.h" #include "common/debug.h" #include "compiler/translator/Diagnostics.h" @@ -25,13 +26,15 @@ static TBehavior getBehavior(const std::string& str) return EBhUndefined; } -TDirectiveHandler::TDirectiveHandler(TExtensionBehavior& extBehavior, - TDiagnostics& diagnostics, - int& shaderVersion, +TDirectiveHandler::TDirectiveHandler(TExtensionBehavior &extBehavior, + TDiagnostics &diagnostics, + int &shaderVersion, + sh::GLenum shaderType, bool debugShaderPrecisionSupported) : mExtensionBehavior(extBehavior), mDiagnostics(diagnostics), mShaderVersion(shaderVersion), + mShaderType(shaderType), mDebugShaderPrecisionSupported(debugShaderPrecisionSupported) { } @@ -57,7 +60,16 @@ void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc, const char kAll[] = "all"; if (name == kInvariant && value == kAll) + { + if (mShaderVersion == 300 && mShaderType == GL_FRAGMENT_SHADER) + { + // ESSL 3.00.4 section 4.6.1 + mDiagnostics.writeInfo( + pp::Diagnostics::PP_ERROR, loc, + "#pragma STDGL invariant(all) can not be used in fragment shader", name, value); + } mPragma.stdgl.invariantAll = true; + } // The STDGL pragma is used to reserve pragmas for use by future // revisions of GLSL. Do not generate an error on unexpected // name and value. diff --git a/gfx/angle/src/compiler/translator/DirectiveHandler.h b/gfx/angle/src/compiler/translator/DirectiveHandler.h index 5f98ac7823..00eb49114e 100644 --- a/gfx/angle/src/compiler/translator/DirectiveHandler.h +++ b/gfx/angle/src/compiler/translator/DirectiveHandler.h @@ -11,15 +11,17 @@ #include "compiler/translator/ExtensionBehavior.h" #include "compiler/translator/Pragma.h" #include "compiler/preprocessor/DirectiveHandlerBase.h" +#include "GLSLANG/ShaderLang.h" class TDiagnostics; class TDirectiveHandler : public pp::DirectiveHandler, angle::NonCopyable { public: - TDirectiveHandler(TExtensionBehavior& extBehavior, - TDiagnostics& diagnostics, - int& shaderVersion, + TDirectiveHandler(TExtensionBehavior &extBehavior, + TDiagnostics &diagnostics, + int &shaderVersion, + sh::GLenum shaderType, bool debugShaderPrecisionSupported); ~TDirectiveHandler() override; @@ -44,6 +46,7 @@ class TDirectiveHandler : public pp::DirectiveHandler, angle::NonCopyable TExtensionBehavior& mExtensionBehavior; TDiagnostics& mDiagnostics; int& mShaderVersion; + sh::GLenum mShaderType; bool mDebugShaderPrecisionSupported; }; diff --git a/gfx/angle/src/compiler/translator/ForLoopUnroll.cpp b/gfx/angle/src/compiler/translator/ForLoopUnroll.cpp index f3be20d978..4cc1c26a13 100644 --- a/gfx/angle/src/compiler/translator/ForLoopUnroll.cpp +++ b/gfx/angle/src/compiler/translator/ForLoopUnroll.cpp @@ -6,6 +6,9 @@ #include "compiler/translator/ForLoopUnroll.h" +#include "compiler/translator/ValidateLimitations.h" +#include "angle_gl.h" + bool ForLoopUnrollMarker::visitBinary(Visit, TIntermBinary *node) { if (mUnrollCondition != kSamplerArrayIndex) @@ -38,11 +41,16 @@ bool ForLoopUnrollMarker::visitBinary(Visit, TIntermBinary *node) bool ForLoopUnrollMarker::visitLoop(Visit, TIntermLoop *node) { - if (mUnrollCondition == kIntegerIndex) + bool canBeUnrolled = mHasRunLoopValidation; + if (!mHasRunLoopValidation) + { + canBeUnrolled = ValidateLimitations::IsLimitedForLoop(node); + } + if (mUnrollCondition == kIntegerIndex && canBeUnrolled) { // Check if loop index type is integer. - // This is called after ValidateLimitations pass, so all the calls - // should be valid. See ValidateLimitations::validateForLoopInit(). + // This is called after ValidateLimitations pass, so the loop has the limited form specified + // in ESSL 1.00 appendix A. TIntermSequence *declSeq = node->getInit()->getAsAggregate()->getSequence(); TIntermSymbol *symbol = (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode(); if (symbol->getBasicType() == EbtInt) @@ -50,11 +58,18 @@ bool ForLoopUnrollMarker::visitLoop(Visit, TIntermLoop *node) } TIntermNode *body = node->getBody(); - if (body != NULL) + if (body != nullptr) { - mLoopStack.push(node); - body->traverse(this); - mLoopStack.pop(); + if (canBeUnrolled) + { + mLoopStack.push(node); + body->traverse(this); + mLoopStack.pop(); + } + else + { + body->traverse(this); + } } // The loop is fully processed - no need to visit children. return false; diff --git a/gfx/angle/src/compiler/translator/ForLoopUnroll.h b/gfx/angle/src/compiler/translator/ForLoopUnroll.h index 84894d4e46..9c49ecad33 100644 --- a/gfx/angle/src/compiler/translator/ForLoopUnroll.h +++ b/gfx/angle/src/compiler/translator/ForLoopUnroll.h @@ -24,11 +24,12 @@ class ForLoopUnrollMarker : public TIntermTraverser kSamplerArrayIndex }; - ForLoopUnrollMarker(UnrollCondition condition) + ForLoopUnrollMarker(UnrollCondition condition, bool hasRunLoopValidation) : TIntermTraverser(true, false, false), mUnrollCondition(condition), mSamplerArrayIndexIsFloatLoopIndex(false), - mVisitSamplerArrayIndexNodeInsideLoop(false) + mVisitSamplerArrayIndexNodeInsideLoop(false), + mHasRunLoopValidation(hasRunLoopValidation) { } @@ -46,6 +47,7 @@ class ForLoopUnrollMarker : public TIntermTraverser TLoopStack mLoopStack; bool mSamplerArrayIndexIsFloatLoopIndex; bool mVisitSamplerArrayIndexNodeInsideLoop; + bool mHasRunLoopValidation; }; #endif // COMPILER_TRANSLATOR_FORLOOPUNROLL_H_ diff --git a/gfx/angle/src/compiler/translator/OutputHLSL.cpp b/gfx/angle/src/compiler/translator/OutputHLSL.cpp index 7e8302f264..253b96696c 100644 --- a/gfx/angle/src/compiler/translator/OutputHLSL.cpp +++ b/gfx/angle/src/compiler/translator/OutputHLSL.cpp @@ -83,19 +83,9 @@ TString OutputHLSL::TextureFunction::name() const { TString name = "gl_texture"; - if (IsSampler2D(sampler)) - { - name += "2D"; - } - else if (IsSampler3D(sampler)) - { - name += "3D"; - } - else if (IsSamplerCube(sampler)) - { - name += "Cube"; - } - else UNREACHABLE(); + // We need to include full the sampler type in the function name to make the signature unique + // on D3D11, where samplers are passed to texture functions as indices. + name += TextureTypeSuffix(this->sampler); if (proj) { @@ -185,7 +175,7 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, mStructureHLSL = new StructureHLSL; mUniformHLSL = new UniformHLSL(mStructureHLSL, outputType, uniforms); - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { // Fragment shaders need dx_DepthRange, dx_ViewCoords and dx_DepthFront. // Vertex shaders need a slightly different set: dx_DepthRange, dx_ViewCoords and dx_ViewAdjust. @@ -239,7 +229,7 @@ void OutputHLSL::output(TIntermNode *treeRoot, TInfoSinkBase &objSink) mInfoSinkStack.pop(); mInfoSinkStack.push(&mHeader); - header(&builtInFunctionEmulator); + header(mHeader, &builtInFunctionEmulator); mInfoSinkStack.pop(); objSink << mHeader.c_str(); @@ -334,10 +324,8 @@ TString OutputHLSL::structInitializerString(int indent, const TStructure &struct return init; } -void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) +void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator) { - TInfoSinkBase &out = getInfoSink(); - TString varyings; TString attributes; TString flaggedStructs; @@ -374,7 +362,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << mStructureHLSL->structsHeader(); - out << mUniformHLSL->uniformsHeader(mOutputType, mReferencedUniforms); + mUniformHLSL->uniformsHeader(out, mOutputType, mReferencedUniforms); out << mUniformHLSL->interfaceBlocksHeader(mReferencedInterfaceBlocks); if (!mEqualityFunctions.empty()) @@ -497,7 +485,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) "\n"; } - if (mOutputType == SH_HLSL11_OUTPUT) + if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { out << "cbuffer DriverConstants : register(b1)\n" "{\n"; @@ -517,6 +505,13 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " float3 dx_DepthFront : packoffset(c2);\n"; } + if (mUsesFragCoord) + { + // dx_ViewScale is only used in the fragment shader to correct + // the value for glFragCoord if necessary + out << " float2 dx_ViewScale : packoffset(c3);\n"; + } + out << "};\n"; } else @@ -601,7 +596,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) "\n"; } - if (mOutputType == SH_HLSL11_OUTPUT) + if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { out << "cbuffer DriverConstants : register(b1)\n" "{\n"; @@ -611,11 +606,13 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " float3 dx_DepthRange : packoffset(c0);\n"; } - // dx_ViewAdjust and dx_ViewCoords will only be used in Feature Level 9 shaders. - // However, we declare it for all shaders (including Feature Level 10+). - // The bytecode is the same whether we declare it or not, since D3DCompiler removes it if it's unused. + // dx_ViewAdjust and dx_ViewCoords will only be used in Feature Level 9 + // shaders. However, we declare it for all shaders (including Feature Level 10+). + // The bytecode is the same whether we declare it or not, since D3DCompiler removes it + // if it's unused. out << " float4 dx_ViewAdjust : packoffset(c1);\n"; out << " float2 dx_ViewCoords : packoffset(c2);\n"; + out << " float2 dx_ViewScale : packoffset(c3);\n"; out << "};\n" "\n"; @@ -701,7 +698,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) // Argument list int hlslCoords = 4; - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { switch(textureFunction->sampler) { @@ -720,29 +717,20 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) default: UNREACHABLE(); } } - else if (mOutputType == SH_HLSL11_OUTPUT) + else { - switch(textureFunction->sampler) + hlslCoords = HLSLTextureCoordsCount(textureFunction->sampler); + if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { - case EbtSampler2D: out << "Texture2D x, SamplerState s"; hlslCoords = 2; break; - case EbtSampler3D: out << "Texture3D x, SamplerState s"; hlslCoords = 3; break; - case EbtSamplerCube: out << "TextureCube x, SamplerState s"; hlslCoords = 3; break; - case EbtSampler2DArray: out << "Texture2DArray x, SamplerState s"; hlslCoords = 3; break; - case EbtISampler2D: out << "Texture2D x, SamplerState s"; hlslCoords = 2; break; - case EbtISampler3D: out << "Texture3D x, SamplerState s"; hlslCoords = 3; break; - case EbtISamplerCube: out << "Texture2DArray x, SamplerState s"; hlslCoords = 3; break; - case EbtISampler2DArray: out << "Texture2DArray x, SamplerState s"; hlslCoords = 3; break; - case EbtUSampler2D: out << "Texture2D x, SamplerState s"; hlslCoords = 2; break; - case EbtUSampler3D: out << "Texture3D x, SamplerState s"; hlslCoords = 3; break; - case EbtUSamplerCube: out << "Texture2DArray x, SamplerState s"; hlslCoords = 3; break; - case EbtUSampler2DArray: out << "Texture2DArray x, SamplerState s"; hlslCoords = 3; break; - case EbtSampler2DShadow: out << "Texture2D x, SamplerComparisonState s"; hlslCoords = 2; break; - case EbtSamplerCubeShadow: out << "TextureCube x, SamplerComparisonState s"; hlslCoords = 3; break; - case EbtSampler2DArrayShadow: out << "Texture2DArray x, SamplerComparisonState s"; hlslCoords = 3; break; - default: UNREACHABLE(); + out << TextureString(textureFunction->sampler) << " x, " + << SamplerString(textureFunction->sampler) << " s"; + } + else + { + ASSERT(mOutputType == SH_HLSL_4_1_OUTPUT); + out << "const uint samplerIndex"; } } - else UNREACHABLE(); if (textureFunction->method == TextureFunction::FETCH) // Integer coordinates { @@ -833,6 +821,31 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << ")\n" "{\n"; + // In some cases we use a variable to store the texture/sampler objects, but to work around + // a D3D11 compiler bug related to discard inside a loop that is conditional on texture + // sampling we need to call the function directly on a reference to the array. The bug was + // found using dEQP-GLES3.functional.shaders.discard*loop_texture* tests. + TString textureReference("x"); + TString samplerReference("s"); + if (mOutputType == SH_HLSL_4_1_OUTPUT) + { + TString suffix = TextureGroupSuffix(textureFunction->sampler); + if (TextureGroup(textureFunction->sampler) == HLSL_TEXTURE_2D) + { + textureReference = TString("textures") + suffix + "[samplerIndex]"; + samplerReference = TString("samplers") + suffix + "[samplerIndex]"; + } + else + { + out << " const uint textureIndex = samplerIndex - textureIndexOffset" << suffix + << ";\n"; + textureReference = TString("textures") + suffix + "[textureIndex]"; + out << " const uint samplerArrayIndex = samplerIndex - samplerIndexOffset" + << suffix << ";\n"; + samplerReference = TString("samplers") + suffix + "[samplerArrayIndex]"; + } + } + if (textureFunction->method == TextureFunction::SIZE) { if (IsSampler2D(textureFunction->sampler) || IsSamplerCube(textureFunction->sampler)) @@ -840,18 +853,21 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) if (IsSamplerArray(textureFunction->sampler)) { out << " uint width; uint height; uint layers; uint numberOfLevels;\n" - " x.GetDimensions(lod, width, height, layers, numberOfLevels);\n"; + << " " << textureReference + << ".GetDimensions(lod, width, height, layers, numberOfLevels);\n"; } else { out << " uint width; uint height; uint numberOfLevels;\n" - " x.GetDimensions(lod, width, height, numberOfLevels);\n"; + << " " << textureReference + << ".GetDimensions(lod, width, height, numberOfLevels);\n"; } } else if (IsSampler3D(textureFunction->sampler)) { out << " uint width; uint height; uint depth; uint numberOfLevels;\n" - " x.GetDimensions(lod, width, height, depth, numberOfLevels);\n"; + << " " << textureReference + << ".GetDimensions(lod, width, height, depth, numberOfLevels);\n"; } else UNREACHABLE(); @@ -883,7 +899,8 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " uint mip = 0;\n"; - out << " x.GetDimensions(mip, width, height, layers, levels);\n"; + out << " " << textureReference + << ".GetDimensions(mip, width, height, layers, levels);\n"; out << " bool xMajor = abs(t.x) > abs(t.y) && abs(t.x) > abs(t.z);\n"; out << " bool yMajor = abs(t.y) > abs(t.z) && abs(t.y) > abs(t.x);\n"; @@ -904,6 +921,18 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " t.x = (u * 0.5f / m) + 0.5f;\n"; out << " t.y = (v * 0.5f / m) + 0.5f;\n"; + + // Mip level computation. + if (textureFunction->method == TextureFunction::IMPLICIT) + { + out << " float2 tSized = float2(t.x * width, t.y * height);\n" + " float2 dx = ddx(tSized);\n" + " float2 dy = ddy(tSized);\n" + " float lod = 0.5f * log2(max(dot(dx, dx), dot(dy, dy)));\n" + " mip = uint(min(max(round(lod), 0), levels - 1));\n" + << " " << textureReference + << ".GetDimensions(mip, width, height, layers, levels);\n"; + } } else if (IsIntegerSampler(textureFunction->sampler) && textureFunction->method != TextureFunction::FETCH) @@ -925,7 +954,8 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) else { - out << " x.GetDimensions(0, width, height, layers, levels);\n"; + out << " " << textureReference + << ".GetDimensions(0, width, height, layers, levels);\n"; if (textureFunction->method == TextureFunction::IMPLICIT || textureFunction->method == TextureFunction::BIAS) { @@ -947,7 +977,8 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; } - out << " x.GetDimensions(mip, width, height, layers, levels);\n"; + out << " " << textureReference + << ".GetDimensions(mip, width, height, layers, levels);\n"; } else { @@ -963,7 +994,8 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else { - out << " x.GetDimensions(0, width, height, levels);\n"; + out << " " << textureReference + << ".GetDimensions(0, width, height, levels);\n"; if (textureFunction->method == TextureFunction::IMPLICIT || textureFunction->method == TextureFunction::BIAS) @@ -986,7 +1018,8 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; } - out << " x.GetDimensions(mip, width, height, levels);\n"; + out << " " << textureReference + << ".GetDimensions(mip, width, height, levels);\n"; } } else if (IsSampler3D(textureFunction->sampler)) @@ -1003,7 +1036,8 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else { - out << " x.GetDimensions(0, width, height, depth, levels);\n"; + out << " " << textureReference + << ".GetDimensions(0, width, height, depth, levels);\n"; if (textureFunction->method == TextureFunction::IMPLICIT || textureFunction->method == TextureFunction::BIAS) @@ -1027,7 +1061,8 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; } - out << " x.GetDimensions(mip, width, height, depth, levels);\n"; + out << " " << textureReference + << ".GetDimensions(mip, width, height, depth, levels);\n"; } else UNREACHABLE(); } @@ -1035,7 +1070,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " return "; // HLSL intrinsic - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { switch(textureFunction->sampler) { @@ -1046,45 +1081,71 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) switch(textureFunction->method) { - case TextureFunction::IMPLICIT: out << "(s, "; break; - case TextureFunction::BIAS: out << "bias(s, "; break; - case TextureFunction::LOD: out << "lod(s, "; break; - case TextureFunction::LOD0: out << "lod(s, "; break; - case TextureFunction::LOD0BIAS: out << "lod(s, "; break; + case TextureFunction::IMPLICIT: + out << "(" << samplerReference << ", "; + break; + case TextureFunction::BIAS: + out << "bias(" << samplerReference << ", "; + break; + case TextureFunction::LOD: + out << "lod(" << samplerReference << ", "; + break; + case TextureFunction::LOD0: + out << "lod(" << samplerReference << ", "; + break; + case TextureFunction::LOD0BIAS: + out << "lod(" << samplerReference << ", "; + break; default: UNREACHABLE(); } } - else if (mOutputType == SH_HLSL11_OUTPUT) + else if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { if (textureFunction->method == TextureFunction::GRAD) { if (IsIntegerSampler(textureFunction->sampler)) { - out << "x.Load("; + out << "" << textureReference << ".Load("; } else if (IsShadowSampler(textureFunction->sampler)) { - out << "x.SampleCmpLevelZero(s, "; + out << "" << textureReference << ".SampleCmpLevelZero(" << samplerReference + << ", "; } else { - out << "x.SampleGrad(s, "; + out << "" << textureReference << ".SampleGrad(" << samplerReference << ", "; } } else if (IsIntegerSampler(textureFunction->sampler) || textureFunction->method == TextureFunction::FETCH) { - out << "x.Load("; + out << "" << textureReference << ".Load("; } else if (IsShadowSampler(textureFunction->sampler)) { switch(textureFunction->method) { - case TextureFunction::IMPLICIT: out << "x.SampleCmp(s, "; break; - case TextureFunction::BIAS: out << "x.SampleCmp(s, "; break; - case TextureFunction::LOD: out << "x.SampleCmp(s, "; break; - case TextureFunction::LOD0: out << "x.SampleCmpLevelZero(s, "; break; - case TextureFunction::LOD0BIAS: out << "x.SampleCmpLevelZero(s, "; break; + case TextureFunction::IMPLICIT: + out << "" << textureReference << ".SampleCmp(" << samplerReference + << ", "; + break; + case TextureFunction::BIAS: + out << "" << textureReference << ".SampleCmp(" << samplerReference + << ", "; + break; + case TextureFunction::LOD: + out << "" << textureReference << ".SampleCmp(" << samplerReference + << ", "; + break; + case TextureFunction::LOD0: + out << "" << textureReference << ".SampleCmpLevelZero(" + << samplerReference << ", "; + break; + case TextureFunction::LOD0BIAS: + out << "" << textureReference << ".SampleCmpLevelZero(" + << samplerReference << ", "; + break; default: UNREACHABLE(); } } @@ -1092,11 +1153,25 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) { switch(textureFunction->method) { - case TextureFunction::IMPLICIT: out << "x.Sample(s, "; break; - case TextureFunction::BIAS: out << "x.SampleBias(s, "; break; - case TextureFunction::LOD: out << "x.SampleLevel(s, "; break; - case TextureFunction::LOD0: out << "x.SampleLevel(s, "; break; - case TextureFunction::LOD0BIAS: out << "x.SampleLevel(s, "; break; + case TextureFunction::IMPLICIT: + out << "" << textureReference << ".Sample(" << samplerReference << ", "; + break; + case TextureFunction::BIAS: + out << "" << textureReference << ".SampleBias(" << samplerReference + << ", "; + break; + case TextureFunction::LOD: + out << "" << textureReference << ".SampleLevel(" << samplerReference + << ", "; + break; + case TextureFunction::LOD0: + out << "" << textureReference << ".SampleLevel(" << samplerReference + << ", "; + break; + case TextureFunction::LOD0BIAS: + out << "" << textureReference << ".SampleLevel(" << samplerReference + << ", "; + break; default: UNREACHABLE(); } } @@ -1166,7 +1241,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << addressx + ("t.x" + proj) + close + ", " + addressy + ("t.y" + proj) + close; - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { if (hlslCoords >= 3) { @@ -1194,7 +1269,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << "));\n"; } - else if (mOutputType == SH_HLSL11_OUTPUT) + else if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { if (hlslCoords >= 3) { @@ -1445,11 +1520,11 @@ void OutputHLSL::outputEqual(Visit visit, const TType &type, TOperator op, TInfo { if (op == EOpEqual) { - outputTriplet(visit, "(", " == ", ")", out); + outputTriplet(out, visit, "(", " == ", ")"); } else { - outputTriplet(visit, "(", " != ", ")", out); + outputTriplet(out, visit, "(", " != ", ")"); } } else @@ -1462,18 +1537,18 @@ void OutputHLSL::outputEqual(Visit visit, const TType &type, TOperator op, TInfo if (type.isArray()) { const TString &functionName = addArrayEqualityFunction(type); - outputTriplet(visit, (functionName + "(").c_str(), ", ", ")", out); + outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); } else if (type.getBasicType() == EbtStruct) { const TStructure &structure = *type.getStruct(); const TString &functionName = addStructEqualityFunction(structure); - outputTriplet(visit, (functionName + "(").c_str(), ", ", ")", out); + outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); } else { ASSERT(type.isMatrix() || type.isVector()); - outputTriplet(visit, "all(", " == ", ")", out); + outputTriplet(out, visit, "all(", " == ", ")"); } } } @@ -1513,11 +1588,11 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) ASSERT(rightAgg == nullptr || rightAgg->getOp() != EOpFunctionCall); const TString &functionName = addArrayAssignmentFunction(node->getType()); - outputTriplet(visit, (functionName + "(").c_str(), ", ", ")"); + outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); } else { - outputTriplet(visit, "(", " = ", ")"); + outputTriplet(out, visit, "(", " = ", ")"); } break; case EOpInitialize: @@ -1560,11 +1635,21 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) out << " = "; } break; - case EOpAddAssign: outputTriplet(visit, "(", " += ", ")"); break; - case EOpSubAssign: outputTriplet(visit, "(", " -= ", ")"); break; - case EOpMulAssign: outputTriplet(visit, "(", " *= ", ")"); break; - case EOpVectorTimesScalarAssign: outputTriplet(visit, "(", " *= ", ")"); break; - case EOpMatrixTimesScalarAssign: outputTriplet(visit, "(", " *= ", ")"); break; + case EOpAddAssign: + outputTriplet(out, visit, "(", " += ", ")"); + break; + case EOpSubAssign: + outputTriplet(out, visit, "(", " -= ", ")"); + break; + case EOpMulAssign: + outputTriplet(out, visit, "(", " *= ", ")"); + break; + case EOpVectorTimesScalarAssign: + outputTriplet(out, visit, "(", " *= ", ")"); + break; + case EOpMatrixTimesScalarAssign: + outputTriplet(out, visit, "(", " *= ", ")"); + break; case EOpVectorTimesMatrixAssign: if (visit == PreVisit) { @@ -1597,13 +1682,27 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) out << "))))"; } break; - case EOpDivAssign: outputTriplet(visit, "(", " /= ", ")"); break; - case EOpIModAssign: outputTriplet(visit, "(", " %= ", ")"); break; - case EOpBitShiftLeftAssign: outputTriplet(visit, "(", " <<= ", ")"); break; - case EOpBitShiftRightAssign: outputTriplet(visit, "(", " >>= ", ")"); break; - case EOpBitwiseAndAssign: outputTriplet(visit, "(", " &= ", ")"); break; - case EOpBitwiseXorAssign: outputTriplet(visit, "(", " ^= ", ")"); break; - case EOpBitwiseOrAssign: outputTriplet(visit, "(", " |= ", ")"); break; + case EOpDivAssign: + outputTriplet(out, visit, "(", " /= ", ")"); + break; + case EOpIModAssign: + outputTriplet(out, visit, "(", " %= ", ")"); + break; + case EOpBitShiftLeftAssign: + outputTriplet(out, visit, "(", " <<= ", ")"); + break; + case EOpBitShiftRightAssign: + outputTriplet(out, visit, "(", " >>= ", ")"); + break; + case EOpBitwiseAndAssign: + outputTriplet(out, visit, "(", " &= ", ")"); + break; + case EOpBitwiseXorAssign: + outputTriplet(out, visit, "(", " ^= ", ")"); + break; + case EOpBitwiseOrAssign: + outputTriplet(out, visit, "(", " |= ", ")"); + break; case EOpIndexDirect: { const TType& leftType = node->getLeft()->getType(); @@ -1620,14 +1719,14 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) } else { - outputTriplet(visit, "", "[", "]"); + outputTriplet(out, visit, "", "[", "]"); } } break; case EOpIndexIndirect: // We do not currently support indirect references to interface blocks ASSERT(node->getLeft()->getBasicType() != EbtInterfaceBlock); - outputTriplet(visit, "", "[", "]"); + outputTriplet(out, visit, "", "[", "]"); break; case EOpIndexDirectStruct: if (visit == InVisit) @@ -1687,42 +1786,80 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) return false; // Fully processed } break; - case EOpAdd: outputTriplet(visit, "(", " + ", ")"); break; - case EOpSub: outputTriplet(visit, "(", " - ", ")"); break; - case EOpMul: outputTriplet(visit, "(", " * ", ")"); break; - case EOpDiv: outputTriplet(visit, "(", " / ", ")"); break; - case EOpIMod: outputTriplet(visit, "(", " % ", ")"); break; - case EOpBitShiftLeft: outputTriplet(visit, "(", " << ", ")"); break; - case EOpBitShiftRight: outputTriplet(visit, "(", " >> ", ")"); break; - case EOpBitwiseAnd: outputTriplet(visit, "(", " & ", ")"); break; - case EOpBitwiseXor: outputTriplet(visit, "(", " ^ ", ")"); break; - case EOpBitwiseOr: outputTriplet(visit, "(", " | ", ")"); break; + case EOpAdd: + outputTriplet(out, visit, "(", " + ", ")"); + break; + case EOpSub: + outputTriplet(out, visit, "(", " - ", ")"); + break; + case EOpMul: + outputTriplet(out, visit, "(", " * ", ")"); + break; + case EOpDiv: + outputTriplet(out, visit, "(", " / ", ")"); + break; + case EOpIMod: + outputTriplet(out, visit, "(", " % ", ")"); + break; + case EOpBitShiftLeft: + outputTriplet(out, visit, "(", " << ", ")"); + break; + case EOpBitShiftRight: + outputTriplet(out, visit, "(", " >> ", ")"); + break; + case EOpBitwiseAnd: + outputTriplet(out, visit, "(", " & ", ")"); + break; + case EOpBitwiseXor: + outputTriplet(out, visit, "(", " ^ ", ")"); + break; + case EOpBitwiseOr: + outputTriplet(out, visit, "(", " | ", ")"); + break; case EOpEqual: case EOpNotEqual: outputEqual(visit, node->getLeft()->getType(), node->getOp(), out); break; - case EOpLessThan: outputTriplet(visit, "(", " < ", ")"); break; - case EOpGreaterThan: outputTriplet(visit, "(", " > ", ")"); break; - case EOpLessThanEqual: outputTriplet(visit, "(", " <= ", ")"); break; - case EOpGreaterThanEqual: outputTriplet(visit, "(", " >= ", ")"); break; - case EOpVectorTimesScalar: outputTriplet(visit, "(", " * ", ")"); break; - case EOpMatrixTimesScalar: outputTriplet(visit, "(", " * ", ")"); break; - case EOpVectorTimesMatrix: outputTriplet(visit, "mul(", ", transpose(", "))"); break; - case EOpMatrixTimesVector: outputTriplet(visit, "mul(transpose(", "), ", ")"); break; - case EOpMatrixTimesMatrix: outputTriplet(visit, "transpose(mul(transpose(", "), transpose(", ")))"); break; + case EOpLessThan: + outputTriplet(out, visit, "(", " < ", ")"); + break; + case EOpGreaterThan: + outputTriplet(out, visit, "(", " > ", ")"); + break; + case EOpLessThanEqual: + outputTriplet(out, visit, "(", " <= ", ")"); + break; + case EOpGreaterThanEqual: + outputTriplet(out, visit, "(", " >= ", ")"); + break; + case EOpVectorTimesScalar: + outputTriplet(out, visit, "(", " * ", ")"); + break; + case EOpMatrixTimesScalar: + outputTriplet(out, visit, "(", " * ", ")"); + break; + case EOpVectorTimesMatrix: + outputTriplet(out, visit, "mul(", ", transpose(", "))"); + break; + case EOpMatrixTimesVector: + outputTriplet(out, visit, "mul(transpose(", "), ", ")"); + break; + case EOpMatrixTimesMatrix: + outputTriplet(out, visit, "transpose(mul(transpose(", "), transpose(", ")))"); + break; case EOpLogicalOr: // HLSL doesn't short-circuit ||, so we assume that || affected by short-circuiting have been unfolded. ASSERT(!node->getRight()->hasSideEffects()); - outputTriplet(visit, "(", " || ", ")"); + outputTriplet(out, visit, "(", " || ", ")"); return true; case EOpLogicalXor: mUsesXor = true; - outputTriplet(visit, "xor(", ", ", ")"); + outputTriplet(out, visit, "xor(", ", ", ")"); break; case EOpLogicalAnd: // HLSL doesn't short-circuit &&, so we assume that && affected by short-circuiting have been unfolded. ASSERT(!node->getRight()->hasSideEffects()); - outputTriplet(visit, "(", " && ", ")"); + outputTriplet(out, visit, "(", " && ", ")"); return true; default: UNREACHABLE(); } @@ -1732,131 +1869,221 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node) { + TInfoSinkBase &out = getInfoSink(); + switch (node->getOp()) { - case EOpNegative: outputTriplet(visit, "(-", "", ")"); break; - case EOpPositive: outputTriplet(visit, "(+", "", ")"); break; - case EOpVectorLogicalNot: outputTriplet(visit, "(!", "", ")"); break; - case EOpLogicalNot: outputTriplet(visit, "(!", "", ")"); break; - case EOpBitwiseNot: outputTriplet(visit, "(~", "", ")"); break; - case EOpPostIncrement: outputTriplet(visit, "(", "", "++)"); break; - case EOpPostDecrement: outputTriplet(visit, "(", "", "--)"); break; - case EOpPreIncrement: outputTriplet(visit, "(++", "", ")"); break; - case EOpPreDecrement: outputTriplet(visit, "(--", "", ")"); break; - case EOpRadians: outputTriplet(visit, "radians(", "", ")"); break; - case EOpDegrees: outputTriplet(visit, "degrees(", "", ")"); break; - case EOpSin: outputTriplet(visit, "sin(", "", ")"); break; - case EOpCos: outputTriplet(visit, "cos(", "", ")"); break; - case EOpTan: outputTriplet(visit, "tan(", "", ")"); break; - case EOpAsin: outputTriplet(visit, "asin(", "", ")"); break; - case EOpAcos: outputTriplet(visit, "acos(", "", ")"); break; - case EOpAtan: outputTriplet(visit, "atan(", "", ")"); break; - case EOpSinh: outputTriplet(visit, "sinh(", "", ")"); break; - case EOpCosh: outputTriplet(visit, "cosh(", "", ")"); break; - case EOpTanh: outputTriplet(visit, "tanh(", "", ")"); break; + case EOpNegative: + outputTriplet(out, visit, "(-", "", ")"); + break; + case EOpPositive: + outputTriplet(out, visit, "(+", "", ")"); + break; + case EOpVectorLogicalNot: + outputTriplet(out, visit, "(!", "", ")"); + break; + case EOpLogicalNot: + outputTriplet(out, visit, "(!", "", ")"); + break; + case EOpBitwiseNot: + outputTriplet(out, visit, "(~", "", ")"); + break; + case EOpPostIncrement: + outputTriplet(out, visit, "(", "", "++)"); + break; + case EOpPostDecrement: + outputTriplet(out, visit, "(", "", "--)"); + break; + case EOpPreIncrement: + outputTriplet(out, visit, "(++", "", ")"); + break; + case EOpPreDecrement: + outputTriplet(out, visit, "(--", "", ")"); + break; + case EOpRadians: + outputTriplet(out, visit, "radians(", "", ")"); + break; + case EOpDegrees: + outputTriplet(out, visit, "degrees(", "", ")"); + break; + case EOpSin: + outputTriplet(out, visit, "sin(", "", ")"); + break; + case EOpCos: + outputTriplet(out, visit, "cos(", "", ")"); + break; + case EOpTan: + outputTriplet(out, visit, "tan(", "", ")"); + break; + case EOpAsin: + outputTriplet(out, visit, "asin(", "", ")"); + break; + case EOpAcos: + outputTriplet(out, visit, "acos(", "", ")"); + break; + case EOpAtan: + outputTriplet(out, visit, "atan(", "", ")"); + break; + case EOpSinh: + outputTriplet(out, visit, "sinh(", "", ")"); + break; + case EOpCosh: + outputTriplet(out, visit, "cosh(", "", ")"); + break; + case EOpTanh: + outputTriplet(out, visit, "tanh(", "", ")"); + break; case EOpAsinh: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "asinh("); + writeEmulatedFunctionTriplet(out, visit, "asinh("); break; case EOpAcosh: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "acosh("); + writeEmulatedFunctionTriplet(out, visit, "acosh("); break; case EOpAtanh: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "atanh("); + writeEmulatedFunctionTriplet(out, visit, "atanh("); break; - case EOpExp: outputTriplet(visit, "exp(", "", ")"); break; - case EOpLog: outputTriplet(visit, "log(", "", ")"); break; - case EOpExp2: outputTriplet(visit, "exp2(", "", ")"); break; - case EOpLog2: outputTriplet(visit, "log2(", "", ")"); break; - case EOpSqrt: outputTriplet(visit, "sqrt(", "", ")"); break; - case EOpInverseSqrt: outputTriplet(visit, "rsqrt(", "", ")"); break; - case EOpAbs: outputTriplet(visit, "abs(", "", ")"); break; - case EOpSign: outputTriplet(visit, "sign(", "", ")"); break; - case EOpFloor: outputTriplet(visit, "floor(", "", ")"); break; - case EOpTrunc: outputTriplet(visit, "trunc(", "", ")"); break; - case EOpRound: outputTriplet(visit, "round(", "", ")"); break; + case EOpExp: + outputTriplet(out, visit, "exp(", "", ")"); + break; + case EOpLog: + outputTriplet(out, visit, "log(", "", ")"); + break; + case EOpExp2: + outputTriplet(out, visit, "exp2(", "", ")"); + break; + case EOpLog2: + outputTriplet(out, visit, "log2(", "", ")"); + break; + case EOpSqrt: + outputTriplet(out, visit, "sqrt(", "", ")"); + break; + case EOpInverseSqrt: + outputTriplet(out, visit, "rsqrt(", "", ")"); + break; + case EOpAbs: + outputTriplet(out, visit, "abs(", "", ")"); + break; + case EOpSign: + outputTriplet(out, visit, "sign(", "", ")"); + break; + case EOpFloor: + outputTriplet(out, visit, "floor(", "", ")"); + break; + case EOpTrunc: + outputTriplet(out, visit, "trunc(", "", ")"); + break; + case EOpRound: + outputTriplet(out, visit, "round(", "", ")"); + break; case EOpRoundEven: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "roundEven("); + writeEmulatedFunctionTriplet(out, visit, "roundEven("); break; - case EOpCeil: outputTriplet(visit, "ceil(", "", ")"); break; - case EOpFract: outputTriplet(visit, "frac(", "", ")"); break; + case EOpCeil: + outputTriplet(out, visit, "ceil(", "", ")"); + break; + case EOpFract: + outputTriplet(out, visit, "frac(", "", ")"); + break; case EOpIsNan: - outputTriplet(visit, "isnan(", "", ")"); + outputTriplet(out, visit, "isnan(", "", ")"); mRequiresIEEEStrictCompiling = true; break; - case EOpIsInf: outputTriplet(visit, "isinf(", "", ")"); break; - case EOpFloatBitsToInt: outputTriplet(visit, "asint(", "", ")"); break; - case EOpFloatBitsToUint: outputTriplet(visit, "asuint(", "", ")"); break; - case EOpIntBitsToFloat: outputTriplet(visit, "asfloat(", "", ")"); break; - case EOpUintBitsToFloat: outputTriplet(visit, "asfloat(", "", ")"); break; + case EOpIsInf: + outputTriplet(out, visit, "isinf(", "", ")"); + break; + case EOpFloatBitsToInt: + outputTriplet(out, visit, "asint(", "", ")"); + break; + case EOpFloatBitsToUint: + outputTriplet(out, visit, "asuint(", "", ")"); + break; + case EOpIntBitsToFloat: + outputTriplet(out, visit, "asfloat(", "", ")"); + break; + case EOpUintBitsToFloat: + outputTriplet(out, visit, "asfloat(", "", ")"); + break; case EOpPackSnorm2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "packSnorm2x16("); + writeEmulatedFunctionTriplet(out, visit, "packSnorm2x16("); break; case EOpPackUnorm2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "packUnorm2x16("); + writeEmulatedFunctionTriplet(out, visit, "packUnorm2x16("); break; case EOpPackHalf2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "packHalf2x16("); + writeEmulatedFunctionTriplet(out, visit, "packHalf2x16("); break; case EOpUnpackSnorm2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "unpackSnorm2x16("); + writeEmulatedFunctionTriplet(out, visit, "unpackSnorm2x16("); break; case EOpUnpackUnorm2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "unpackUnorm2x16("); + writeEmulatedFunctionTriplet(out, visit, "unpackUnorm2x16("); break; case EOpUnpackHalf2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "unpackHalf2x16("); + writeEmulatedFunctionTriplet(out, visit, "unpackHalf2x16("); break; - case EOpLength: outputTriplet(visit, "length(", "", ")"); break; - case EOpNormalize: outputTriplet(visit, "normalize(", "", ")"); break; + case EOpLength: + outputTriplet(out, visit, "length(", "", ")"); + break; + case EOpNormalize: + outputTriplet(out, visit, "normalize(", "", ")"); + break; case EOpDFdx: if(mInsideDiscontinuousLoop || mOutputLod0Function) { - outputTriplet(visit, "(", "", ", 0.0)"); + outputTriplet(out, visit, "(", "", ", 0.0)"); } else { - outputTriplet(visit, "ddx(", "", ")"); + outputTriplet(out, visit, "ddx(", "", ")"); } break; case EOpDFdy: if(mInsideDiscontinuousLoop || mOutputLod0Function) { - outputTriplet(visit, "(", "", ", 0.0)"); + outputTriplet(out, visit, "(", "", ", 0.0)"); } else { - outputTriplet(visit, "ddy(", "", ")"); + outputTriplet(out, visit, "ddy(", "", ")"); } break; case EOpFwidth: if(mInsideDiscontinuousLoop || mOutputLod0Function) { - outputTriplet(visit, "(", "", ", 0.0)"); + outputTriplet(out, visit, "(", "", ", 0.0)"); } else { - outputTriplet(visit, "fwidth(", "", ")"); + outputTriplet(out, visit, "fwidth(", "", ")"); } break; - case EOpTranspose: outputTriplet(visit, "transpose(", "", ")"); break; - case EOpDeterminant: outputTriplet(visit, "determinant(transpose(", "", "))"); break; + case EOpTranspose: + outputTriplet(out, visit, "transpose(", "", ")"); + break; + case EOpDeterminant: + outputTriplet(out, visit, "determinant(transpose(", "", "))"); + break; case EOpInverse: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "inverse("); + writeEmulatedFunctionTriplet(out, visit, "inverse("); break; - case EOpAny: outputTriplet(visit, "any(", "", ")"); break; - case EOpAll: outputTriplet(visit, "all(", "", ")"); break; + case EOpAny: + outputTriplet(out, visit, "any(", "", ")"); + break; + case EOpAll: + outputTriplet(out, visit, "all(", "", ")"); + break; default: UNREACHABLE(); } @@ -1873,13 +2100,13 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) { if (mInsideFunction) { - outputLineDirective(node->getLine().first_line); + outputLineDirective(out, node->getLine().first_line); out << "{\n"; } for (TIntermSequence::iterator sit = node->getSequence()->begin(); sit != node->getSequence()->end(); sit++) { - outputLineDirective((*sit)->getLine().first_line); + outputLineDirective(out, (*sit)->getLine().first_line); (*sit)->traverse(this); @@ -1896,7 +2123,7 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) if (mInsideFunction) { - outputLineDirective(node->getLine().last_line); + outputLineDirective(out, node->getLine().last_line); out << "}\n"; } @@ -2017,7 +2244,9 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) return false; } break; - case EOpComma: outputTriplet(visit, "(", ", ", ")"); break; + case EOpComma: + outputTriplet(out, visit, "(", ", ", ")"); + break; case EOpFunction: { ASSERT(mCurrentFunctionMetadata == nullptr); @@ -2230,7 +2459,8 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) for (TIntermSequence::iterator arg = arguments->begin(); arg != arguments->end(); arg++) { - if (mOutputType == SH_HLSL11_OUTPUT && IsSampler((*arg)->getAsTyped()->getBasicType())) + if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT && + IsSampler((*arg)->getAsTyped()->getBasicType())) { out << "texture_"; (*arg)->traverse(this); @@ -2250,32 +2480,84 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) return false; } break; - case EOpParameters: outputTriplet(visit, "(", ", ", ")\n{\n"); break; - case EOpConstructFloat: outputConstructor(visit, node->getType(), "vec1", node->getSequence()); break; - case EOpConstructVec2: outputConstructor(visit, node->getType(), "vec2", node->getSequence()); break; - case EOpConstructVec3: outputConstructor(visit, node->getType(), "vec3", node->getSequence()); break; - case EOpConstructVec4: outputConstructor(visit, node->getType(), "vec4", node->getSequence()); break; - case EOpConstructBool: outputConstructor(visit, node->getType(), "bvec1", node->getSequence()); break; - case EOpConstructBVec2: outputConstructor(visit, node->getType(), "bvec2", node->getSequence()); break; - case EOpConstructBVec3: outputConstructor(visit, node->getType(), "bvec3", node->getSequence()); break; - case EOpConstructBVec4: outputConstructor(visit, node->getType(), "bvec4", node->getSequence()); break; - case EOpConstructInt: outputConstructor(visit, node->getType(), "ivec1", node->getSequence()); break; - case EOpConstructIVec2: outputConstructor(visit, node->getType(), "ivec2", node->getSequence()); break; - case EOpConstructIVec3: outputConstructor(visit, node->getType(), "ivec3", node->getSequence()); break; - case EOpConstructIVec4: outputConstructor(visit, node->getType(), "ivec4", node->getSequence()); break; - case EOpConstructUInt: outputConstructor(visit, node->getType(), "uvec1", node->getSequence()); break; - case EOpConstructUVec2: outputConstructor(visit, node->getType(), "uvec2", node->getSequence()); break; - case EOpConstructUVec3: outputConstructor(visit, node->getType(), "uvec3", node->getSequence()); break; - case EOpConstructUVec4: outputConstructor(visit, node->getType(), "uvec4", node->getSequence()); break; - case EOpConstructMat2: outputConstructor(visit, node->getType(), "mat2", node->getSequence()); break; - case EOpConstructMat2x3: outputConstructor(visit, node->getType(), "mat2x3", node->getSequence()); break; - case EOpConstructMat2x4: outputConstructor(visit, node->getType(), "mat2x4", node->getSequence()); break; - case EOpConstructMat3x2: outputConstructor(visit, node->getType(), "mat3x2", node->getSequence()); break; - case EOpConstructMat3: outputConstructor(visit, node->getType(), "mat3", node->getSequence()); break; - case EOpConstructMat3x4: outputConstructor(visit, node->getType(), "mat3x4", node->getSequence()); break; - case EOpConstructMat4x2: outputConstructor(visit, node->getType(), "mat4x2", node->getSequence()); break; - case EOpConstructMat4x3: outputConstructor(visit, node->getType(), "mat4x3", node->getSequence()); break; - case EOpConstructMat4: outputConstructor(visit, node->getType(), "mat4", node->getSequence()); break; + case EOpParameters: + outputTriplet(out, visit, "(", ", ", ")\n{\n"); + break; + case EOpConstructFloat: + outputConstructor(out, visit, node->getType(), "vec1", node->getSequence()); + break; + case EOpConstructVec2: + outputConstructor(out, visit, node->getType(), "vec2", node->getSequence()); + break; + case EOpConstructVec3: + outputConstructor(out, visit, node->getType(), "vec3", node->getSequence()); + break; + case EOpConstructVec4: + outputConstructor(out, visit, node->getType(), "vec4", node->getSequence()); + break; + case EOpConstructBool: + outputConstructor(out, visit, node->getType(), "bvec1", node->getSequence()); + break; + case EOpConstructBVec2: + outputConstructor(out, visit, node->getType(), "bvec2", node->getSequence()); + break; + case EOpConstructBVec3: + outputConstructor(out, visit, node->getType(), "bvec3", node->getSequence()); + break; + case EOpConstructBVec4: + outputConstructor(out, visit, node->getType(), "bvec4", node->getSequence()); + break; + case EOpConstructInt: + outputConstructor(out, visit, node->getType(), "ivec1", node->getSequence()); + break; + case EOpConstructIVec2: + outputConstructor(out, visit, node->getType(), "ivec2", node->getSequence()); + break; + case EOpConstructIVec3: + outputConstructor(out, visit, node->getType(), "ivec3", node->getSequence()); + break; + case EOpConstructIVec4: + outputConstructor(out, visit, node->getType(), "ivec4", node->getSequence()); + break; + case EOpConstructUInt: + outputConstructor(out, visit, node->getType(), "uvec1", node->getSequence()); + break; + case EOpConstructUVec2: + outputConstructor(out, visit, node->getType(), "uvec2", node->getSequence()); + break; + case EOpConstructUVec3: + outputConstructor(out, visit, node->getType(), "uvec3", node->getSequence()); + break; + case EOpConstructUVec4: + outputConstructor(out, visit, node->getType(), "uvec4", node->getSequence()); + break; + case EOpConstructMat2: + outputConstructor(out, visit, node->getType(), "mat2", node->getSequence()); + break; + case EOpConstructMat2x3: + outputConstructor(out, visit, node->getType(), "mat2x3", node->getSequence()); + break; + case EOpConstructMat2x4: + outputConstructor(out, visit, node->getType(), "mat2x4", node->getSequence()); + break; + case EOpConstructMat3x2: + outputConstructor(out, visit, node->getType(), "mat3x2", node->getSequence()); + break; + case EOpConstructMat3: + outputConstructor(out, visit, node->getType(), "mat3", node->getSequence()); + break; + case EOpConstructMat3x4: + outputConstructor(out, visit, node->getType(), "mat3x4", node->getSequence()); + break; + case EOpConstructMat4x2: + outputConstructor(out, visit, node->getType(), "mat4x2", node->getSequence()); + break; + case EOpConstructMat4x3: + outputConstructor(out, visit, node->getType(), "mat4x3", node->getSequence()); + break; + case EOpConstructMat4: + outputConstructor(out, visit, node->getType(), "mat4", node->getSequence()); + break; case EOpConstructStruct: { if (node->getType().isArray()) @@ -2284,29 +2566,51 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) } const TString &structName = StructNameString(*node->getType().getStruct()); mStructureHLSL->addConstructor(node->getType(), structName, node->getSequence()); - outputTriplet(visit, (structName + "_ctor(").c_str(), ", ", ")"); + outputTriplet(out, visit, (structName + "_ctor(").c_str(), ", ", ")"); } break; - case EOpLessThan: outputTriplet(visit, "(", " < ", ")"); break; - case EOpGreaterThan: outputTriplet(visit, "(", " > ", ")"); break; - case EOpLessThanEqual: outputTriplet(visit, "(", " <= ", ")"); break; - case EOpGreaterThanEqual: outputTriplet(visit, "(", " >= ", ")"); break; - case EOpVectorEqual: outputTriplet(visit, "(", " == ", ")"); break; - case EOpVectorNotEqual: outputTriplet(visit, "(", " != ", ")"); break; + case EOpLessThan: + outputTriplet(out, visit, "(", " < ", ")"); + break; + case EOpGreaterThan: + outputTriplet(out, visit, "(", " > ", ")"); + break; + case EOpLessThanEqual: + outputTriplet(out, visit, "(", " <= ", ")"); + break; + case EOpGreaterThanEqual: + outputTriplet(out, visit, "(", " >= ", ")"); + break; + case EOpVectorEqual: + outputTriplet(out, visit, "(", " == ", ")"); + break; + case EOpVectorNotEqual: + outputTriplet(out, visit, "(", " != ", ")"); + break; case EOpMod: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "mod("); + writeEmulatedFunctionTriplet(out, visit, "mod("); break; - case EOpModf: outputTriplet(visit, "modf(", ", ", ")"); break; - case EOpPow: outputTriplet(visit, "pow(", ", ", ")"); break; + case EOpModf: + outputTriplet(out, visit, "modf(", ", ", ")"); + break; + case EOpPow: + outputTriplet(out, visit, "pow(", ", ", ")"); + break; case EOpAtan: ASSERT(node->getSequence()->size() == 2); // atan(x) is a unary operator ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "atan("); + writeEmulatedFunctionTriplet(out, visit, "atan("); break; - case EOpMin: outputTriplet(visit, "min(", ", ", ")"); break; - case EOpMax: outputTriplet(visit, "max(", ", ", ")"); break; - case EOpClamp: outputTriplet(visit, "clamp(", ", ", ")"); break; + case EOpMin: + outputTriplet(out, visit, "min(", ", ", ")"); + break; + case EOpMax: + outputTriplet(out, visit, "max(", ", ", ")"); + break; + case EOpClamp: + outputTriplet(out, visit, "clamp(", ", ", ")"); + break; case EOpMix: { TIntermTyped *lastParamNode = (*(node->getSequence()))[2]->getAsTyped(); @@ -2315,47 +2619,61 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) // There is no HLSL equivalent for ESSL3 built-in "genType mix (genType x, genType y, genBType a)", // so use emulated version. ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "mix("); + writeEmulatedFunctionTriplet(out, visit, "mix("); } else { - outputTriplet(visit, "lerp(", ", ", ")"); + outputTriplet(out, visit, "lerp(", ", ", ")"); } } break; - case EOpStep: outputTriplet(visit, "step(", ", ", ")"); break; - case EOpSmoothStep: outputTriplet(visit, "smoothstep(", ", ", ")"); break; - case EOpDistance: outputTriplet(visit, "distance(", ", ", ")"); break; - case EOpDot: outputTriplet(visit, "dot(", ", ", ")"); break; - case EOpCross: outputTriplet(visit, "cross(", ", ", ")"); break; + case EOpStep: + outputTriplet(out, visit, "step(", ", ", ")"); + break; + case EOpSmoothStep: + outputTriplet(out, visit, "smoothstep(", ", ", ")"); + break; + case EOpDistance: + outputTriplet(out, visit, "distance(", ", ", ")"); + break; + case EOpDot: + outputTriplet(out, visit, "dot(", ", ", ")"); + break; + case EOpCross: + outputTriplet(out, visit, "cross(", ", ", ")"); + break; case EOpFaceForward: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "faceforward("); + writeEmulatedFunctionTriplet(out, visit, "faceforward("); break; - case EOpReflect: outputTriplet(visit, "reflect(", ", ", ")"); break; - case EOpRefract: outputTriplet(visit, "refract(", ", ", ")"); break; + case EOpReflect: + outputTriplet(out, visit, "reflect(", ", ", ")"); + break; + case EOpRefract: + outputTriplet(out, visit, "refract(", ", ", ")"); + break; case EOpOuterProduct: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "outerProduct("); + writeEmulatedFunctionTriplet(out, visit, "outerProduct("); break; - case EOpMul: outputTriplet(visit, "(", " * ", ")"); break; + case EOpMul: + outputTriplet(out, visit, "(", " * ", ")"); + break; default: UNREACHABLE(); } return true; } -void OutputHLSL::writeSelection(TIntermSelection *node) +void OutputHLSL::writeSelection(TInfoSinkBase &out, TIntermSelection *node) { - TInfoSinkBase &out = getInfoSink(); - out << "if ("; node->getCondition()->traverse(this); out << ")\n"; - outputLineDirective(node->getLine().first_line); + outputLineDirective(out, node->getLine().first_line); bool discard = false; @@ -2376,20 +2694,20 @@ void OutputHLSL::writeSelection(TIntermSelection *node) out << "{;}\n"; } - outputLineDirective(node->getLine().first_line); + outputLineDirective(out, node->getLine().first_line); if (node->getFalseBlock()) { out << "else\n"; - outputLineDirective(node->getFalseBlock()->getLine().first_line); + outputLineDirective(out, node->getFalseBlock()->getLine().first_line); // Either this is "else if" or the falseBlock child node will output braces. ASSERT(IsSequence(node->getFalseBlock()) || node->getFalseBlock()->getAsSelectionNode() != nullptr); node->getFalseBlock()->traverse(this); - outputLineDirective(node->getFalseBlock()->getLine().first_line); + outputLineDirective(out, node->getFalseBlock()->getLine().first_line); // Detect false discard discard = (discard || FindDiscard::search(node->getFalseBlock())); @@ -2421,37 +2739,40 @@ bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node) out << "FLATTEN "; } - writeSelection(node); + writeSelection(out, node); return false; } bool OutputHLSL::visitSwitch(Visit visit, TIntermSwitch *node) { + TInfoSinkBase &out = getInfoSink(); + if (node->getStatementList()) { node->setStatementList(RemoveSwitchFallThrough::removeFallThrough(node->getStatementList())); - outputTriplet(visit, "switch (", ") ", ""); + outputTriplet(out, visit, "switch (", ") ", ""); // The curly braces get written when visiting the statementList aggregate } else { // No statementList, so it won't output curly braces - outputTriplet(visit, "switch (", ") {", "}\n"); + outputTriplet(out, visit, "switch (", ") {", "}\n"); } return true; } bool OutputHLSL::visitCase(Visit visit, TIntermCase *node) { + TInfoSinkBase &out = getInfoSink(); + if (node->hasCondition()) { - outputTriplet(visit, "case (", "", "):\n"); + outputTriplet(out, visit, "case (", "", "):\n"); return true; } else { - TInfoSinkBase &out = getInfoSink(); out << "default:\n"; return false; } @@ -2459,7 +2780,8 @@ bool OutputHLSL::visitCase(Visit visit, TIntermCase *node) void OutputHLSL::visitConstantUnion(TIntermConstantUnion *node) { - writeConstantUnion(node->getType(), node->getUnionArrayPointer()); + TInfoSinkBase &out = getInfoSink(); + writeConstantUnion(out, node->getType(), node->getUnionArrayPointer()); } bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) @@ -2470,9 +2792,11 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) mInsideDiscontinuousLoop = mInsideDiscontinuousLoop || mCurrentFunctionMetadata->mDiscontinuousLoops.count(node) > 0; - if (mOutputType == SH_HLSL9_OUTPUT) + TInfoSinkBase &out = getInfoSink(); + + if (mOutputType == SH_HLSL_3_0_OUTPUT) { - if (handleExcessiveLoop(node)) + if (handleExcessiveLoop(out, node)) { mInsideDiscontinuousLoop = wasDiscontinuous; mNestedLoopDepth--; @@ -2481,14 +2805,12 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) } } - TInfoSinkBase &out = getInfoSink(); - const char *unroll = mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : ""; if (node->getType() == ELoopDoWhile) { out << "{" << unroll << " do\n"; - outputLineDirective(node->getLine().first_line); + outputLineDirective(out, node->getLine().first_line); } else { @@ -2515,7 +2837,7 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) out << ")\n"; - outputLineDirective(node->getLine().first_line); + outputLineDirective(out, node->getLine().first_line); } if (node->getBody()) @@ -2531,11 +2853,11 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) out << "{;}\n"; } - outputLineDirective(node->getLine().first_line); + outputLineDirective(out, node->getLine().first_line); if (node->getType() == ELoopDoWhile) { - outputLineDirective(node->getCondition()->getLine().first_line); + outputLineDirective(out, node->getCondition()->getLine().first_line); out << "while(\n"; node->getCondition()->traverse(this); @@ -2558,7 +2880,7 @@ bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node) switch (node->getFlowOp()) { case EOpKill: - outputTriplet(visit, "discard;\n", "", ""); + outputTriplet(out, visit, "discard;\n", "", ""); break; case EOpBreak: if (visit == PreVisit) @@ -2580,7 +2902,9 @@ bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node) } } break; - case EOpContinue: outputTriplet(visit, "continue;\n", "", ""); break; + case EOpContinue: + outputTriplet(out, visit, "continue;\n", "", ""); + break; case EOpReturn: if (visit == PreVisit) { @@ -2642,10 +2966,9 @@ bool OutputHLSL::isSingleStatement(TIntermNode *node) // Handle loops with more than 254 iterations (unsupported by D3D9) by splitting them // (The D3D documentation says 255 iterations, but the compiler complains at anything more than 254). -bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node) +bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) { const int MAX_LOOP_ITERATIONS = 254; - TInfoSinkBase &out = getInfoSink(); // Parse loops of the form: // for(int index = initial; index [comparator] limit; index += increment) @@ -2812,7 +3135,7 @@ bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node) out << increment; out << ")\n"; - outputLineDirective(node->getLine().first_line); + outputLineDirective(out, node->getLine().first_line); out << "{\n"; if (node->getBody()) @@ -2820,7 +3143,7 @@ bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node) node->getBody()->traverse(this); } - outputLineDirective(node->getLine().first_line); + outputLineDirective(out, node->getLine().first_line); out << ";}\n"; if (!firstLoopFragment) @@ -2846,7 +3169,11 @@ bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node) return false; // Not handled as an excessive loop } -void OutputHLSL::outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString, TInfoSinkBase &out) +void OutputHLSL::outputTriplet(TInfoSinkBase &out, + Visit visit, + const char *preString, + const char *inString, + const char *postString) { if (visit == PreVisit) { @@ -2862,17 +3189,10 @@ void OutputHLSL::outputTriplet(Visit visit, const char *preString, const char *i } } -void OutputHLSL::outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString) -{ - outputTriplet(visit, preString, inString, postString, getInfoSink()); -} - -void OutputHLSL::outputLineDirective(int line) +void OutputHLSL::outputLineDirective(TInfoSinkBase &out, int line) { if ((mCompileOptions & SH_LINE_DIRECTIVES) && (line > 0)) { - TInfoSinkBase &out = getInfoSink(); - out << "\n"; out << "#line " << line; @@ -2901,11 +3221,21 @@ TString OutputHLSL::argumentString(const TIntermSymbol *symbol) nameStr = DecorateIfNeeded(name); } - if (mOutputType == SH_HLSL11_OUTPUT && IsSampler(type.getBasicType())) + if (IsSampler(type.getBasicType())) { - return QualifierString(qualifier) + " " + TextureString(type) + " texture_" + nameStr + - ArrayString(type) + ", " + QualifierString(qualifier) + " " + SamplerString(type) + - " sampler_" + nameStr + ArrayString(type); + if (mOutputType == SH_HLSL_4_1_OUTPUT) + { + // Samplers are passed as indices to the sampler array. + ASSERT(qualifier != EvqOut && qualifier != EvqInOut); + return "const uint " + nameStr + ArrayString(type); + } + if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) + { + return QualifierString(qualifier) + " " + TextureString(type.getBasicType()) + + " texture_" + nameStr + ArrayString(type) + ", " + QualifierString(qualifier) + + " " + SamplerString(type.getBasicType()) + " sampler_" + nameStr + + ArrayString(type); + } } return QualifierString(qualifier) + " " + TypeString(type) + " " + nameStr + ArrayString(type); @@ -2929,13 +3259,16 @@ TString OutputHLSL::initializer(const TType &type) return "{" + string + "}"; } -void OutputHLSL::outputConstructor(Visit visit, const TType &type, const char *name, const TIntermSequence *parameters) +void OutputHLSL::outputConstructor(TInfoSinkBase &out, + Visit visit, + const TType &type, + const char *name, + const TIntermSequence *parameters) { if (type.isArray()) { UNIMPLEMENTED(); } - TInfoSinkBase &out = getInfoSink(); if (visit == PreVisit) { @@ -2953,11 +3286,10 @@ void OutputHLSL::outputConstructor(Visit visit, const TType &type, const char *n } } -const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, +const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out, + const TType &type, const TConstantUnion *const constUnion) { - TInfoSinkBase &out = getInfoSink(); - const TConstantUnion *constUnionIterated = constUnion; const TStructure* structure = type.getStruct(); @@ -2970,7 +3302,7 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, for (size_t i = 0; i < fields.size(); i++) { const TType *fieldType = fields[i]->type(); - constUnionIterated = writeConstantUnion(*fieldType, constUnionIterated); + constUnionIterated = writeConstantUnion(out, *fieldType, constUnionIterated); if (i != fields.size() - 1) { @@ -2999,10 +3331,10 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, return constUnionIterated; } -void OutputHLSL::writeEmulatedFunctionTriplet(Visit visit, const char *preStr) +void OutputHLSL::writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, const char *preStr) { TString preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preStr); - outputTriplet(visit, preString.c_str(), ", ", ")"); + outputTriplet(out, visit, preString.c_str(), ", ", ")"); } bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression) @@ -3117,8 +3449,7 @@ void OutputHLSL::writeDeferredGlobalInitializers(TInfoSinkBase &out) } else if (selection != nullptr) { - ASSERT(mInfoSinkStack.top() == &out); - writeSelection(selection); + writeSelection(out, selection); } else { diff --git a/gfx/angle/src/compiler/translator/OutputHLSL.h b/gfx/angle/src/compiler/translator/OutputHLSL.h index 6d321f1887..8756d0ba4c 100644 --- a/gfx/angle/src/compiler/translator/OutputHLSL.h +++ b/gfx/angle/src/compiler/translator/OutputHLSL.h @@ -50,7 +50,7 @@ class OutputHLSL : public TIntermTraverser static bool canWriteAsHLSLLiteral(TIntermTyped *expression); protected: - void header(const BuiltInFunctionEmulator *builtInFunctionEmulator); + void header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator); // Visit AST nodes and output their code to the body stream void visitSymbol(TIntermSymbol*); @@ -66,22 +66,31 @@ class OutputHLSL : public TIntermTraverser bool visitBranch(Visit visit, TIntermBranch*); bool isSingleStatement(TIntermNode *node); - bool handleExcessiveLoop(TIntermLoop *node); + bool handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node); // Emit one of three strings depending on traverse phase. Called with literal strings so using const char* instead of TString. - void outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString, TInfoSinkBase &out); - void outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString); - void outputLineDirective(int line); + void outputTriplet(TInfoSinkBase &out, + Visit visit, + const char *preString, + const char *inString, + const char *postString); + void outputLineDirective(TInfoSinkBase &out, int line); TString argumentString(const TIntermSymbol *symbol); int vectorSize(const TType &type) const; // Emit constructor. Called with literal names so using const char* instead of TString. - void outputConstructor(Visit visit, const TType &type, const char *name, const TIntermSequence *parameters); - const TConstantUnion *writeConstantUnion(const TType &type, const TConstantUnion *constUnion); + void outputConstructor(TInfoSinkBase &out, + Visit visit, + const TType &type, + const char *name, + const TIntermSequence *parameters); + const TConstantUnion *writeConstantUnion(TInfoSinkBase &out, + const TType &type, + const TConstantUnion *constUnion); void outputEqual(Visit visit, const TType &type, TOperator op, TInfoSinkBase &out); - void writeEmulatedFunctionTriplet(Visit visit, const char *preStr); + void writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, const char *preStr); void makeFlaggedStructMaps(const std::vector &flaggedStructs); // Returns true if it found a 'same symbol' initializer (initializer that references the variable it's initting) @@ -92,7 +101,7 @@ class OutputHLSL : public TIntermTraverser TIntermTyped *expression); void writeDeferredGlobalInitializers(TInfoSinkBase &out); - void writeSelection(TIntermSelection *node); + void writeSelection(TInfoSinkBase &out, TIntermSelection *node); // Returns the function name TString addStructEqualityFunction(const TStructure &structure); diff --git a/gfx/angle/src/compiler/translator/ParseContext.cpp b/gfx/angle/src/compiler/translator/ParseContext.cpp index 8e4d487662..235351cf41 100644 --- a/gfx/angle/src/compiler/translator/ParseContext.cpp +++ b/gfx/angle/src/compiler/translator/ParseContext.cpp @@ -226,24 +226,25 @@ bool TParseContext::precisionErrorCheck(const TSourceLoc &line, { if (!mChecksPrecisionErrors) return false; - switch (type) + if (precision == EbpUndefined) { - case EbtFloat: - if (precision == EbpUndefined) - { + switch (type) + { + case EbtFloat: error(line, "No precision specified for (float)", ""); return true; - } - break; - case EbtInt: - if (precision == EbpUndefined) - { + case EbtInt: + case EbtUInt: + UNREACHABLE(); // there's always a predeclared qualifier error(line, "No precision specified (int)", ""); return true; - } - break; - default: - return false; + default: + if (IsSampler(type)) + { + error(line, "No precision specified (sampler)", ""); + return true; + } + } } return false; } @@ -1924,6 +1925,84 @@ void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier) } } +TIntermAggregate *TParseContext::addFunctionPrototypeDeclaration(const TFunction &function, + const TSourceLoc &location) +{ + // Note: symbolTableFunction could be the same as function if this is the first declaration. + // Either way the instance in the symbol table is used to track whether the function is declared + // multiple times. + TFunction *symbolTableFunction = + static_cast(symbolTable.find(function.getMangledName(), getShaderVersion())); + if (symbolTableFunction->hasPrototypeDeclaration() && mShaderVersion == 100) + { + // ESSL 1.00.17 section 4.2.7. + // Doesn't apply to ESSL 3.00.4: see section 4.2.3. + error(location, "duplicate function prototype declarations are not allowed", "function"); + recover(); + } + symbolTableFunction->setHasPrototypeDeclaration(); + + TIntermAggregate *prototype = new TIntermAggregate; + prototype->setType(function.getReturnType()); + prototype->setName(function.getMangledName()); + prototype->setFunctionId(function.getUniqueId()); + + for (size_t i = 0; i < function.getParamCount(); i++) + { + const TConstParameter ¶m = function.getParam(i); + if (param.name != 0) + { + TVariable variable(param.name, *param.type); + + TIntermSymbol *paramSymbol = intermediate.addSymbol( + variable.getUniqueId(), variable.getName(), variable.getType(), location); + prototype = intermediate.growAggregate(prototype, paramSymbol, location); + } + else + { + TIntermSymbol *paramSymbol = intermediate.addSymbol(0, "", *param.type, location); + prototype = intermediate.growAggregate(prototype, paramSymbol, location); + } + } + + prototype->setOp(EOpPrototype); + + symbolTable.pop(); + + if (!symbolTable.atGlobalLevel()) + { + // ESSL 3.00.4 section 4.2.4. + error(location, "local function prototype declarations are not allowed", "function"); + recover(); + } + + return prototype; +} + +TIntermAggregate *TParseContext::addFunctionDefinition(const TFunction &function, + TIntermAggregate *functionPrototype, + TIntermAggregate *functionBody, + const TSourceLoc &location) +{ + //?? Check that all paths return a value if return type != void ? + // May be best done as post process phase on intermediate code + if (mCurrentFunctionType->getBasicType() != EbtVoid && !mFunctionReturnsValue) + { + error(location, "function does not return a value:", "", function.getName().c_str()); + recover(); + } + + TIntermAggregate *aggregate = + intermediate.growAggregate(functionPrototype, functionBody, location); + intermediate.setAggregateOperator(aggregate, EOpFunction, location); + aggregate->setName(function.getMangledName().c_str()); + aggregate->setType(function.getReturnType()); + aggregate->setFunctionId(function.getUniqueId()); + + symbolTable.pop(); + return aggregate; +} + void TParseContext::parseFunctionPrototype(const TSourceLoc &location, TFunction *function, TIntermAggregate **aggregateOut) @@ -1977,8 +2056,8 @@ void TParseContext::parseFunctionPrototype(const TSourceLoc &location, // // Remember the return type for later checking for RETURN statements. // - setCurrentFunctionType(&(prevDec->getReturnType())); - setFunctionReturnsValue(false); + mCurrentFunctionType = &(prevDec->getReturnType()); + mFunctionReturnsValue = false; // // Insert parameters into the symbol table. @@ -2029,12 +2108,12 @@ void TParseContext::parseFunctionPrototype(const TSourceLoc &location, TFunction *TParseContext::parseFunctionDeclarator(const TSourceLoc &location, TFunction *function) { // - // Multiple declarations of the same function are allowed. + // We don't know at this point whether this is a function definition or a prototype. + // The definition production code will check for redefinitions. + // In the case of ESSL 1.00 the prototype production code will also check for redeclarations. // - // If this is a definition, the definition production code will check for redefinitions - // (we don't know at this point if it's a definition or not). - // - // Redeclarations are allowed. But, return types and parameter qualifiers must match. + // Return types and parameter qualifiers must match in all redeclarations, so those are checked + // here. // TFunction *prevDec = static_cast(symbolTable.find(function->getMangledName(), getShaderVersion())); diff --git a/gfx/angle/src/compiler/translator/ParseContext.h b/gfx/angle/src/compiler/translator/ParseContext.h index da7c42047a..1eaf1e5b42 100644 --- a/gfx/angle/src/compiler/translator/ParseContext.h +++ b/gfx/angle/src/compiler/translator/ParseContext.h @@ -57,6 +57,7 @@ class TParseContext : angle::NonCopyable mDirectiveHandler(ext, mDiagnostics, mShaderVersion, + mShaderType, resources.WEBGL_debug_shader_precision == 1), mPreprocessor(&mDiagnostics, &mDirectiveHandler), mScanner(nullptr), @@ -102,23 +103,11 @@ class TParseContext : angle::NonCopyable mFragmentPrecisionHighOnESSL1 = fragmentPrecisionHigh; } - bool getFunctionReturnsValue() const { return mFunctionReturnsValue; } - void setFunctionReturnsValue(bool functionReturnsValue) - { - mFunctionReturnsValue = functionReturnsValue; - } - void setLoopNestingLevel(int loopNestintLevel) { mLoopNestingLevel = loopNestintLevel; } - const TType *getCurrentFunctionType() const { return mCurrentFunctionType; } - void setCurrentFunctionType(const TType *currentFunctionType) - { - mCurrentFunctionType = currentFunctionType; - } - void incrLoopNestingLevel() { ++mLoopNestingLevel; } void decrLoopNestingLevel() { --mLoopNestingLevel; } @@ -244,6 +233,12 @@ class TParseContext : angle::NonCopyable TIntermTyped *initializer); void parseGlobalLayoutQualifier(const TPublicType &typeQualifier); + TIntermAggregate *addFunctionPrototypeDeclaration(const TFunction &function, + const TSourceLoc &location); + TIntermAggregate *addFunctionDefinition(const TFunction &function, + TIntermAggregate *functionPrototype, + TIntermAggregate *functionBody, + const TSourceLoc &location); void parseFunctionPrototype(const TSourceLoc &location, TFunction *function, TIntermAggregate **aggregateOut); diff --git a/gfx/angle/src/compiler/translator/ShaderLang.cpp b/gfx/angle/src/compiler/translator/ShaderLang.cpp index 6abfcd1d41..e257f93e48 100644 --- a/gfx/angle/src/compiler/translator/ShaderLang.cpp +++ b/gfx/angle/src/compiler/translator/ShaderLang.cpp @@ -189,9 +189,16 @@ ShHandle ShConstructCompiler(sh::GLenum type, ShShaderSpec spec, const ShBuiltInResources* resources) { TShHandleBase* base = static_cast(ConstructCompiler(type, spec, output)); - TCompiler* compiler = base->getAsCompiler(); - if (compiler == 0) + if (base == nullptr) + { return 0; + } + + TCompiler* compiler = base->getAsCompiler(); + if (compiler == nullptr) + { + return 0; + } // Generate built-in symbol table. if (!compiler->Init(*resources)) { diff --git a/gfx/angle/src/compiler/translator/ShaderVars.cpp b/gfx/angle/src/compiler/translator/ShaderVars.cpp index 7a86af6993..8f931b9bdd 100644 --- a/gfx/angle/src/compiler/translator/ShaderVars.cpp +++ b/gfx/angle/src/compiler/translator/ShaderVars.cpp @@ -393,4 +393,9 @@ InterfaceBlock &InterfaceBlock::operator=(const InterfaceBlock &other) return *this; } +std::string InterfaceBlock::fieldPrefix() const +{ + return instanceName.empty() ? "" : name; } + +} // namespace sh diff --git a/gfx/angle/src/compiler/translator/SymbolTable.cpp b/gfx/angle/src/compiler/translator/SymbolTable.cpp index 396edc6a13..dc8f8e3b6b 100644 --- a/gfx/angle/src/compiler/translator/SymbolTable.cpp +++ b/gfx/angle/src/compiler/translator/SymbolTable.cpp @@ -280,7 +280,7 @@ TPrecision TSymbolTable::getDefaultPrecision(TBasicType type) const int level = static_cast(precisionStack.size()) - 1; assert(level >= 0); // Just to be safe. Should not happen. - // If we dont find anything we return this. Should we error check this? + // If we dont find anything we return this. Some types don't have predefined default precision. TPrecision prec = EbpUndefined; while (level >= 0) { diff --git a/gfx/angle/src/compiler/translator/SymbolTable.h b/gfx/angle/src/compiler/translator/SymbolTable.h index 2f5eb369f8..2706149e3c 100644 --- a/gfx/angle/src/compiler/translator/SymbolTable.h +++ b/gfx/angle/src/compiler/translator/SymbolTable.h @@ -197,12 +197,16 @@ struct TParameter class TFunction : public TSymbol { public: - TFunction(const TString *name, const TType *retType, TOperator tOp = EOpNull, const char *ext = "") + TFunction(const TString *name, + const TType *retType, + TOperator tOp = EOpNull, + const char *ext = "") : TSymbol(name), returnType(retType), mangledName(nullptr), op(tOp), - defined(false) + defined(false), + mHasPrototypeDeclaration(false) { relateToExtension(ext); } @@ -242,14 +246,10 @@ class TFunction : public TSymbol return op; } - void setDefined() - { - defined = true; - } - bool isDefined() - { - return defined; - } + void setDefined() { defined = true; } + bool isDefined() { return defined; } + void setHasPrototypeDeclaration() { mHasPrototypeDeclaration = true; } + bool hasPrototypeDeclaration() const { return mHasPrototypeDeclaration; } size_t getParamCount() const { @@ -269,6 +269,7 @@ class TFunction : public TSymbol mutable const TString *mangledName; TOperator op; bool defined; + bool mHasPrototypeDeclaration; }; // Interface block name sub-symbol @@ -435,6 +436,8 @@ class TSymbolTable : angle::NonCopyable { if (!SupportsPrecision(type.type)) return false; + if (type.type == EbtUInt) + return false; // ESSL 3.00.4 section 4.5.4 if (type.isAggregate()) return false; // Not allowed to set for aggregate types int indexOfLastElement = static_cast(precisionStack.size()) - 1; diff --git a/gfx/angle/src/compiler/translator/TranslatorHLSL.cpp b/gfx/angle/src/compiler/translator/TranslatorHLSL.cpp index 462f887c82..c5d18d21bf 100644 --- a/gfx/angle/src/compiler/translator/TranslatorHLSL.cpp +++ b/gfx/angle/src/compiler/translator/TranslatorHLSL.cpp @@ -47,7 +47,7 @@ void TranslatorHLSL::translate(TIntermNode *root, int compileOptions) // Work around D3D9 bug that would manifest in vertex shaders with selection blocks which // use a vertex attribute as a condition, and some related computation in the else block. - if (getOutputType() == SH_HLSL9_OUTPUT && getShaderType() == GL_VERTEX_SHADER) + if (getOutputType() == SH_HLSL_3_0_OUTPUT && getShaderType() == GL_VERTEX_SHADER) { sh::RewriteElseBlocks(root, getTemporaryIndex()); } diff --git a/gfx/angle/src/compiler/translator/Types.cpp b/gfx/angle/src/compiler/translator/Types.cpp index d6e874219a..87fdfe0d54 100644 --- a/gfx/angle/src/compiler/translator/Types.cpp +++ b/gfx/angle/src/compiler/translator/Types.cpp @@ -59,6 +59,27 @@ bool TStructure::equals(const TStructure &other) const return (uniqueId() == other.uniqueId()); } +TString TType::getCompleteString() const +{ + TStringStream stream; + + if (invariant) + stream << "invariant "; + if (qualifier != EvqTemporary && qualifier != EvqGlobal) + stream << getQualifierString() << " "; + if (precision != EbpUndefined) + stream << getPrecisionString() << " "; + if (array) + stream << "array[" << getArraySize() << "] of "; + if (isMatrix()) + stream << getCols() << "X" << getRows() << " matrix of "; + else if (isVector()) + stream << getNominalSize() << "-component vector of "; + + stream << getBasicString(); + return stream.str(); +} + // // Recursively generate mangled names. // diff --git a/gfx/angle/src/compiler/translator/UniformHLSL.cpp b/gfx/angle/src/compiler/translator/UniformHLSL.cpp index 71659fe354..20961c44c1 100644 --- a/gfx/angle/src/compiler/translator/UniformHLSL.cpp +++ b/gfx/angle/src/compiler/translator/UniformHLSL.cpp @@ -93,7 +93,9 @@ const Uniform *UniformHLSL::findUniformByName(const TString &name) const return NULL; } -unsigned int UniformHLSL::declareUniformAndAssignRegister(const TType &type, const TString &name) +unsigned int UniformHLSL::declareUniformAndAssignRegister(const TType &type, + const TString &name, + unsigned int *registerCount) { unsigned int registerIndex = (IsSampler(type.getBasicType()) ? mSamplerRegister : mUniformRegister); @@ -102,43 +104,119 @@ unsigned int UniformHLSL::declareUniformAndAssignRegister(const TType &type, con mUniformRegisterMap[uniform->name] = registerIndex; - unsigned int registerCount = HLSLVariableRegisterCount(*uniform, mOutputType); + ASSERT(registerCount); + *registerCount = HLSLVariableRegisterCount(*uniform, mOutputType); if (gl::IsSamplerType(uniform->type)) { - mSamplerRegister += registerCount; + mSamplerRegister += *registerCount; } else { - mUniformRegister += registerCount; + mUniformRegister += *registerCount; } return registerIndex; } -TString UniformHLSL::uniformsHeader(ShShaderOutput outputType, const ReferencedSymbols &referencedUniforms) +unsigned int UniformHLSL::declareUniformAndAssignRegister(const TType &type, const TString &name) { - TString uniforms; + unsigned int registerCount; + return declareUniformAndAssignRegister(type, name, ®isterCount); +} - for (ReferencedSymbols::const_iterator uniformIt = referencedUniforms.begin(); - uniformIt != referencedUniforms.end(); uniformIt++) +void UniformHLSL::outputHLSLSamplerUniformGroup(TInfoSinkBase &out, + const HLSLTextureSamplerGroup textureGroup, + const TVector &group, + unsigned int *groupTextureRegisterIndex) +{ + if (group.empty()) { - const TIntermSymbol &uniform = *uniformIt->second; - const TType &type = uniform.getType(); - const TString &name = uniform.getSymbol(); - - unsigned int registerIndex = declareUniformAndAssignRegister(type, name); - - if (outputType == SH_HLSL11_OUTPUT && IsSampler(type.getBasicType())) // Also declare the texture + return; + } + unsigned int groupRegisterCount = 0; + for (const TIntermSymbol *uniform : group) + { + const TType &type = uniform->getType(); + const TString &name = uniform->getSymbol(); + unsigned int registerCount; + unsigned int samplerArrayIndex = + declareUniformAndAssignRegister(type, name, ®isterCount); + groupRegisterCount += registerCount; + if (type.isArray()) { - uniforms += "uniform " + SamplerString(type) + " sampler_" + DecorateUniform(name, type) + ArrayString(type) + - " : register(s" + str(registerIndex) + ");\n"; - - uniforms += "uniform " + TextureString(type) + " texture_" + DecorateUniform(name, type) + ArrayString(type) + - " : register(t" + str(registerIndex) + ");\n"; + out << "static const uint " << DecorateIfNeeded(uniform->getName()) << ArrayString(type) + << " = {"; + for (int i = 0; i < type.getArraySize(); ++i) + { + if (i > 0) + out << ", "; + out << (samplerArrayIndex + i); + } + out << "};\n"; } else { + out << "static const uint " << DecorateIfNeeded(uniform->getName()) << " = " + << samplerArrayIndex << ";\n"; + } + } + TString suffix = TextureGroupSuffix(textureGroup); + // Since HLSL_TEXTURE_2D is the first group, it has a fixed offset of zero. + if (textureGroup != HLSL_TEXTURE_2D) + { + out << "static const uint textureIndexOffset" << suffix << " = " + << (*groupTextureRegisterIndex) << ";\n"; + out << "static const uint samplerIndexOffset" << suffix << " = " + << (*groupTextureRegisterIndex) << ";\n"; + } + out << "uniform " << TextureString(textureGroup) << " textures" << suffix << "[" + << groupRegisterCount << "]" + << " : register(t" << (*groupTextureRegisterIndex) << ");\n"; + out << "uniform " << SamplerString(textureGroup) << " samplers" << suffix << "[" + << groupRegisterCount << "]" + << " : register(s" << (*groupTextureRegisterIndex) << ");\n"; + *groupTextureRegisterIndex += groupRegisterCount; +} + +void UniformHLSL::uniformsHeader(TInfoSinkBase &out, + ShShaderOutput outputType, + const ReferencedSymbols &referencedUniforms) +{ + if (!referencedUniforms.empty()) + { + out << "// Uniforms\n\n"; + } + // In the case of HLSL 4, sampler uniforms need to be grouped by type before the code is + // written. They are grouped based on the combination of the HLSL texture type and + // HLSL sampler type, enumerated in HLSLTextureSamplerGroup. + TVector> groupedSamplerUniforms; + groupedSamplerUniforms.resize(HLSL_TEXTURE_MAX + 1); + for (auto &uniformIt : referencedUniforms) + { + // Output regular uniforms. Group sampler uniforms by type. + const TIntermSymbol &uniform = *uniformIt.second; + const TType &type = uniform.getType(); + const TString &name = uniform.getSymbol(); + + if (outputType == SH_HLSL_4_1_OUTPUT && IsSampler(type.getBasicType())) + { + HLSLTextureSamplerGroup group = TextureGroup(type.getBasicType()); + groupedSamplerUniforms[group].push_back(&uniform); + } + else if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT && IsSampler(type.getBasicType())) + { + unsigned int registerIndex = declareUniformAndAssignRegister(type, name); + out << "uniform " << SamplerString(type.getBasicType()) << " sampler_" + << DecorateUniform(name, type) << ArrayString(type) << " : register(s" + << str(registerIndex) << ");\n"; + out << "uniform " << TextureString(type.getBasicType()) << " texture_" + << DecorateUniform(name, type) << ArrayString(type) << " : register(t" + << str(registerIndex) << ");\n"; + } + else + { + unsigned int registerIndex = declareUniformAndAssignRegister(type, name); const TStructure *structure = type.getStruct(); // If this is a nameless struct, we need to use its full definition, rather than its (empty) name. // TypeString() will invoke defineNameless in this case; qualifier prefixes are unnecessary for @@ -149,11 +227,23 @@ TString UniformHLSL::uniformsHeader(ShShaderOutput outputType, const ReferencedS const TString ®isterString = TString("register(") + UniformRegisterPrefix(type) + str(registerIndex) + ")"; - uniforms += "uniform " + typeName + " " + DecorateUniform(name, type) + ArrayString(type) + " : " + registerString + ";\n"; + out << "uniform " << typeName << " " << DecorateUniform(name, type) << ArrayString(type) + << " : " << registerString << ";\n"; } } - return (uniforms.empty() ? "" : ("// Uniforms\n\n" + uniforms)); + if (outputType == SH_HLSL_4_1_OUTPUT) + { + unsigned int groupTextureRegisterIndex = 0; + // TEXTURE_2D is special, index offset is assumed to be 0 and omitted in that case. + ASSERT(HLSL_TEXTURE_MIN == HLSL_TEXTURE_2D); + for (int groupId = HLSL_TEXTURE_MIN; groupId < HLSL_TEXTURE_MAX; ++groupId) + { + outputHLSLSamplerUniformGroup(out, HLSLTextureSamplerGroup(groupId), + groupedSamplerUniforms[groupId], + &groupTextureRegisterIndex); + } + } } TString UniformHLSL::interfaceBlocksHeader(const ReferencedSymbols &referencedInterfaceBlocks) diff --git a/gfx/angle/src/compiler/translator/UniformHLSL.h b/gfx/angle/src/compiler/translator/UniformHLSL.h index 4ab9ccdf53..0f51f349bb 100644 --- a/gfx/angle/src/compiler/translator/UniformHLSL.h +++ b/gfx/angle/src/compiler/translator/UniformHLSL.h @@ -11,6 +11,7 @@ #define COMPILER_TRANSLATOR_UNIFORMHLSL_H_ #include "compiler/translator/OutputHLSL.h" +#include "compiler/translator/UtilsHLSL.h" namespace sh { @@ -23,7 +24,13 @@ class UniformHLSL : angle::NonCopyable void reserveUniformRegisters(unsigned int registerCount); void reserveInterfaceBlockRegisters(unsigned int registerCount); - TString uniformsHeader(ShShaderOutput outputType, const ReferencedSymbols &referencedUniforms); + void outputHLSLSamplerUniformGroup(TInfoSinkBase &out, + const HLSLTextureSamplerGroup textureGroup, + const TVector &group, + unsigned int *groupTextureRegisterIndex); + void uniformsHeader(TInfoSinkBase &out, + ShShaderOutput outputType, + const ReferencedSymbols &referencedUniforms); TString interfaceBlocksHeader(const ReferencedSymbols &referencedInterfaceBlocks); // Used for direct index references @@ -45,6 +52,9 @@ class UniformHLSL : angle::NonCopyable const Uniform *findUniformByName(const TString &name) const; // Returns the uniform's register index + unsigned int declareUniformAndAssignRegister(const TType &type, + const TString &name, + unsigned int *registerCount); unsigned int declareUniformAndAssignRegister(const TType &type, const TString &name); unsigned int mUniformRegister; diff --git a/gfx/angle/src/compiler/translator/UtilsHLSL.cpp b/gfx/angle/src/compiler/translator/UtilsHLSL.cpp index c0ceef9e99..404ccee75d 100644 --- a/gfx/angle/src/compiler/translator/UtilsHLSL.cpp +++ b/gfx/angle/src/compiler/translator/UtilsHLSL.cpp @@ -15,9 +15,9 @@ namespace sh { -TString SamplerString(const TType &type) +TString SamplerString(const TBasicType type) { - if (IsShadowSampler(type.getBasicType())) + if (IsShadowSampler(type)) { return "SamplerComparisonState"; } @@ -27,32 +27,158 @@ TString SamplerString(const TType &type) } } -TString TextureString(const TType &type) +TString SamplerString(HLSLTextureSamplerGroup type) { - switch (type.getBasicType()) + if (type >= HLSL_COMPARISON_SAMPLER_GROUP_BEGIN && type <= HLSL_COMPARISON_SAMPLER_GROUP_END) { - case EbtSampler2D: return "Texture2D"; - case EbtSamplerCube: return "TextureCube"; - case EbtSamplerExternalOES: return "Texture2D"; - case EbtSampler2DArray: return "Texture2DArray"; - case EbtSampler3D: return "Texture3D"; - case EbtISampler2D: return "Texture2D"; - case EbtISampler3D: return "Texture3D"; - case EbtISamplerCube: return "Texture2DArray"; - case EbtISampler2DArray: return "Texture2DArray"; - case EbtUSampler2D: return "Texture2D"; - case EbtUSampler3D: return "Texture3D"; - case EbtUSamplerCube: return "Texture2DArray"; - case EbtUSampler2DArray: return "Texture2DArray"; - case EbtSampler2DShadow: return "Texture2D"; - case EbtSamplerCubeShadow: return "TextureCube"; - case EbtSampler2DArrayShadow: return "Texture2DArray"; - default: UNREACHABLE(); + return "SamplerComparisonState"; + } + else + { + return "SamplerState"; + } +} + +HLSLTextureSamplerGroup TextureGroup(const TBasicType type) +{ + switch (type) + { + case EbtSampler2D: + return HLSL_TEXTURE_2D; + case EbtSamplerCube: + return HLSL_TEXTURE_CUBE; + case EbtSamplerExternalOES: + return HLSL_TEXTURE_2D; + case EbtSampler2DArray: + return HLSL_TEXTURE_2D_ARRAY; + case EbtSampler3D: + return HLSL_TEXTURE_3D; + case EbtISampler2D: + return HLSL_TEXTURE_2D_INT4; + case EbtISampler3D: + return HLSL_TEXTURE_3D_INT4; + case EbtISamplerCube: + return HLSL_TEXTURE_2D_ARRAY_INT4; + case EbtISampler2DArray: + return HLSL_TEXTURE_2D_ARRAY_INT4; + case EbtUSampler2D: + return HLSL_TEXTURE_2D_UINT4; + case EbtUSampler3D: + return HLSL_TEXTURE_3D_UINT4; + case EbtUSamplerCube: + return HLSL_TEXTURE_2D_ARRAY_UINT4; + case EbtUSampler2DArray: + return HLSL_TEXTURE_2D_ARRAY_UINT4; + case EbtSampler2DShadow: + return HLSL_TEXTURE_2D_COMPARISON; + case EbtSamplerCubeShadow: + return HLSL_TEXTURE_CUBE_COMPARISON; + case EbtSampler2DArrayShadow: + return HLSL_TEXTURE_2D_ARRAY_COMPARISON; + default: + UNREACHABLE(); + } + return HLSL_TEXTURE_UNKNOWN; +} + +TString TextureString(const HLSLTextureSamplerGroup type) +{ + switch (type) + { + case HLSL_TEXTURE_2D: + return "Texture2D"; + case HLSL_TEXTURE_CUBE: + return "TextureCube"; + case HLSL_TEXTURE_2D_ARRAY: + return "Texture2DArray"; + case HLSL_TEXTURE_3D: + return "Texture3D"; + case HLSL_TEXTURE_2D_INT4: + return "Texture2D"; + case HLSL_TEXTURE_3D_INT4: + return "Texture3D"; + case HLSL_TEXTURE_2D_ARRAY_INT4: + return "Texture2DArray"; + case HLSL_TEXTURE_2D_UINT4: + return "Texture2D"; + case HLSL_TEXTURE_3D_UINT4: + return "Texture3D"; + case HLSL_TEXTURE_2D_ARRAY_UINT4: + return "Texture2DArray"; + case HLSL_TEXTURE_2D_COMPARISON: + return "Texture2D"; + case HLSL_TEXTURE_CUBE_COMPARISON: + return "TextureCube"; + case HLSL_TEXTURE_2D_ARRAY_COMPARISON: + return "Texture2DArray"; + default: + UNREACHABLE(); } return ""; } +TString TextureString(const TBasicType type) +{ + return TextureString(TextureGroup(type)); +} + +TString TextureGroupSuffix(const HLSLTextureSamplerGroup type) +{ + switch (type) + { + case HLSL_TEXTURE_2D: + return "2D"; + case HLSL_TEXTURE_CUBE: + return "Cube"; + case HLSL_TEXTURE_2D_ARRAY: + return "2DArray"; + case HLSL_TEXTURE_3D: + return "3D"; + case HLSL_TEXTURE_2D_INT4: + return "2D_int4_"; + case HLSL_TEXTURE_3D_INT4: + return "3D_int4_"; + case HLSL_TEXTURE_2D_ARRAY_INT4: + return "2DArray_int4_"; + case HLSL_TEXTURE_2D_UINT4: + return "2D_uint4_"; + case HLSL_TEXTURE_3D_UINT4: + return "3D_uint4_"; + case HLSL_TEXTURE_2D_ARRAY_UINT4: + return "2DArray_uint4_"; + case HLSL_TEXTURE_2D_COMPARISON: + return "2D_comparison"; + case HLSL_TEXTURE_CUBE_COMPARISON: + return "Cube_comparison"; + case HLSL_TEXTURE_2D_ARRAY_COMPARISON: + return "2DArray_comparison"; + default: + UNREACHABLE(); + } + + return ""; +} + +TString TextureGroupSuffix(const TBasicType type) +{ + return TextureGroupSuffix(TextureGroup(type)); +} + +TString TextureTypeSuffix(const TBasicType type) +{ + switch (type) + { + case EbtISamplerCube: + return "Cube_int4_"; + case EbtUSamplerCube: + return "Cube_uint4_"; + default: + // All other types are identified by their group suffix + return TextureGroupSuffix(type); + } +} + TString DecorateUniform(const TString &string, const TType &type) { if (type.getBasicType() == EbtSamplerExternalOES) @@ -270,4 +396,43 @@ TString QualifierString(TQualifier qualifier) return ""; } +int HLSLTextureCoordsCount(const TBasicType samplerType) +{ + switch (samplerType) + { + case EbtSampler2D: + return 2; + case EbtSampler3D: + return 3; + case EbtSamplerCube: + return 3; + case EbtSampler2DArray: + return 3; + case EbtISampler2D: + return 2; + case EbtISampler3D: + return 3; + case EbtISamplerCube: + return 3; + case EbtISampler2DArray: + return 3; + case EbtUSampler2D: + return 2; + case EbtUSampler3D: + return 3; + case EbtUSamplerCube: + return 3; + case EbtUSampler2DArray: + return 3; + case EbtSampler2DShadow: + return 2; + case EbtSamplerCubeShadow: + return 3; + case EbtSampler2DArrayShadow: + return 3; + default: + UNREACHABLE(); + } + return 0; +} } diff --git a/gfx/angle/src/compiler/translator/UtilsHLSL.h b/gfx/angle/src/compiler/translator/UtilsHLSL.h index 40b45f2871..42444e3a56 100644 --- a/gfx/angle/src/compiler/translator/UtilsHLSL.h +++ b/gfx/angle/src/compiler/translator/UtilsHLSL.h @@ -20,8 +20,44 @@ class TName; namespace sh { -TString TextureString(const TType &type); -TString SamplerString(const TType &type); +// Unique combinations of HLSL Texture type and HLSL Sampler type. +enum HLSLTextureSamplerGroup +{ + // Regular samplers + HLSL_TEXTURE_2D, + HLSL_TEXTURE_MIN = HLSL_TEXTURE_2D, + + HLSL_TEXTURE_CUBE, + HLSL_TEXTURE_2D_ARRAY, + HLSL_TEXTURE_3D, + HLSL_TEXTURE_2D_INT4, + HLSL_TEXTURE_3D_INT4, + HLSL_TEXTURE_2D_ARRAY_INT4, + HLSL_TEXTURE_2D_UINT4, + HLSL_TEXTURE_3D_UINT4, + HLSL_TEXTURE_2D_ARRAY_UINT4, + + // Comparison samplers + + HLSL_TEXTURE_2D_COMPARISON, + HLSL_TEXTURE_CUBE_COMPARISON, + HLSL_TEXTURE_2D_ARRAY_COMPARISON, + + HLSL_COMPARISON_SAMPLER_GROUP_BEGIN = HLSL_TEXTURE_2D_COMPARISON, + HLSL_COMPARISON_SAMPLER_GROUP_END = HLSL_TEXTURE_2D_ARRAY_COMPARISON, + + HLSL_TEXTURE_UNKNOWN, + HLSL_TEXTURE_MAX = HLSL_TEXTURE_UNKNOWN +}; + +HLSLTextureSamplerGroup TextureGroup(const TBasicType type); +TString TextureString(const HLSLTextureSamplerGroup type); +TString TextureString(const TBasicType type); +TString TextureGroupSuffix(const HLSLTextureSamplerGroup type); +TString TextureGroupSuffix(const TBasicType type); +TString TextureTypeSuffix(const TBasicType type); +TString SamplerString(const TBasicType type); +TString SamplerString(HLSLTextureSamplerGroup type); // Prepends an underscore to avoid naming clashes TString Decorate(const TString &string); TString DecorateIfNeeded(const TName &name); @@ -36,7 +72,7 @@ TString QualifiedStructNameString(const TStructure &structure, bool useHLSLRowMa bool useStd140Packing); TString InterpolationString(TQualifier qualifier); TString QualifierString(TQualifier qualifier); - +int HLSLTextureCoordsCount(const TBasicType samplerType); } #endif // COMPILER_TRANSLATOR_UTILSHLSL_H_ diff --git a/gfx/angle/src/compiler/translator/ValidateLimitations.cpp b/gfx/angle/src/compiler/translator/ValidateLimitations.cpp index ed60c3755e..ba8cdd0aa8 100644 --- a/gfx/angle/src/compiler/translator/ValidateLimitations.cpp +++ b/gfx/angle/src/compiler/translator/ValidateLimitations.cpp @@ -53,15 +53,37 @@ class ValidateConstIndexExpr : public TIntermTraverser } // namespace anonymous -ValidateLimitations::ValidateLimitations(sh::GLenum shaderType, - TInfoSinkBase &sink) +ValidateLimitations::ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase *sink) : TIntermTraverser(true, false, false), mShaderType(shaderType), mSink(sink), - mNumErrors(0) + mNumErrors(0), + mValidateIndexing(true), + mValidateInnerLoops(true) { } +// static +bool ValidateLimitations::IsLimitedForLoop(TIntermLoop *loop) +{ + // The shader type doesn't matter in this case. + ValidateLimitations validate(GL_FRAGMENT_SHADER, nullptr); + validate.mValidateIndexing = false; + validate.mValidateInnerLoops = false; + if (!validate.validateLoopType(loop)) + return false; + if (!validate.validateForLoopHeader(loop)) + return false; + TIntermNode *body = loop->getBody(); + if (body != nullptr) + { + validate.mLoopStack.push(loop); + body->traverse(&validate); + validate.mLoopStack.pop(); + } + return (validate.mNumErrors == 0); +} + bool ValidateLimitations::visitBinary(Visit, TIntermBinary *node) { // Check if loop index is modified in the loop body. @@ -72,10 +94,11 @@ bool ValidateLimitations::visitBinary(Visit, TIntermBinary *node) { case EOpIndexDirect: case EOpIndexIndirect: - validateIndexing(node); - break; + if (mValidateIndexing) + validateIndexing(node); + break; default: - break; + break; } return true; } @@ -102,6 +125,9 @@ bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate *node) bool ValidateLimitations::visitLoop(Visit, TIntermLoop *node) { + if (!mValidateInnerLoops) + return true; + if (!validateLoopType(node)) return false; @@ -123,9 +149,12 @@ bool ValidateLimitations::visitLoop(Visit, TIntermLoop *node) void ValidateLimitations::error(TSourceLoc loc, const char *reason, const char *token) { - mSink.prefix(EPrefixError); - mSink.location(loc); - mSink << "'" << token << "' : " << reason << "\n"; + if (mSink) + { + mSink->prefix(EPrefixError); + mSink->location(loc); + (*mSink) << "'" << token << "' : " << reason << "\n"; + } ++mNumErrors; } @@ -433,8 +462,8 @@ bool ValidateLimitations::validateOperation(TIntermOperator *node, bool ValidateLimitations::isConstExpr(TIntermNode *node) { - ASSERT(node != NULL); - return node->getAsConstantUnion() != NULL; + ASSERT(node != nullptr); + return node->getAsConstantUnion() != nullptr && node->getAsTyped()->getQualifier() == EvqConst; } bool ValidateLimitations::isConstIndexExpr(TIntermNode *node) @@ -453,13 +482,6 @@ bool ValidateLimitations::validateIndexing(TIntermBinary *node) bool valid = true; TIntermTyped *index = node->getRight(); - // The index expression must have integral type. - if (!index->isScalarInt()) { - error(index->getLine(), - "Index expression must have integral type", - index->getCompleteString().c_str()); - valid = false; - } // The index expession must be a constant-index-expression unless // the operand is a uniform in a vertex shader. TIntermTyped *operand = node->getLeft(); diff --git a/gfx/angle/src/compiler/translator/ValidateLimitations.h b/gfx/angle/src/compiler/translator/ValidateLimitations.h index ec8891ba1d..666e38ff5c 100644 --- a/gfx/angle/src/compiler/translator/ValidateLimitations.h +++ b/gfx/angle/src/compiler/translator/ValidateLimitations.h @@ -17,7 +17,7 @@ class TInfoSinkBase; class ValidateLimitations : public TIntermTraverser { public: - ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase &sink); + ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase *sink); int numErrors() const { return mNumErrors; } @@ -26,6 +26,8 @@ class ValidateLimitations : public TIntermTraverser bool visitAggregate(Visit, TIntermAggregate *) override; bool visitLoop(Visit, TIntermLoop *) override; + static bool IsLimitedForLoop(TIntermLoop *node); + private: void error(TSourceLoc loc, const char *reason, const char *token); @@ -51,9 +53,11 @@ class ValidateLimitations : public TIntermTraverser bool validateIndexing(TIntermBinary *node); sh::GLenum mShaderType; - TInfoSinkBase &mSink; + TInfoSinkBase *mSink; int mNumErrors; TLoopStack mLoopStack; + bool mValidateIndexing; + bool mValidateInnerLoops; }; #endif // COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_ diff --git a/gfx/angle/src/compiler/translator/VariableInfo.cpp b/gfx/angle/src/compiler/translator/VariableInfo.cpp index eb2a2d8d07..3b6aa6a68e 100644 --- a/gfx/angle/src/compiler/translator/VariableInfo.cpp +++ b/gfx/angle/src/compiler/translator/VariableInfo.cpp @@ -16,18 +16,6 @@ namespace sh namespace { -TString InterfaceBlockFieldName(const TInterfaceBlock &interfaceBlock, const TField &field) -{ - if (interfaceBlock.hasInstanceName()) - { - return interfaceBlock.name() + "." + field.name(); - } - else - { - return field.name(); - } -} - BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage) { switch (blockStorage) @@ -559,16 +547,12 @@ void CollectVariables::visitVariable(const TIntermSymbol *variable, interfaceBlock.layout = GetBlockLayoutType(blockType->blockStorage()); // Gather field information - const TFieldList &fieldList = blockType->fields(); - - for (size_t fieldIndex = 0; fieldIndex < fieldList.size(); ++fieldIndex) + for (const TField *field : blockType->fields()) { - const TField &field = *fieldList[fieldIndex]; - const TString &fullFieldName = InterfaceBlockFieldName(*blockType, field); - const TType &fieldType = *field.type(); + const TType &fieldType = *field->type(); NameHashingTraverser traverser(mHashFunction, mSymbolTable); - traverser.traverse(fieldType, fullFieldName, &interfaceBlock.fields); + traverser.traverse(fieldType, field->name(), &interfaceBlock.fields); interfaceBlock.fields.back().isRowMajorLayout = (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor); } diff --git a/gfx/angle/src/compiler/translator/blocklayout.h b/gfx/angle/src/compiler/translator/blocklayout.h index 6bcb79d3f0..dd5fe07376 100644 --- a/gfx/angle/src/compiler/translator/blocklayout.h +++ b/gfx/angle/src/compiler/translator/blocklayout.h @@ -55,8 +55,6 @@ class COMPILER_EXPORT BlockLayoutEncoder BlockMemberInfo encodeType(GLenum type, unsigned int arraySize, bool isRowMajorMatrix); size_t getBlockSize() const { return mCurrentOffset * BytesPerComponent; } - size_t getCurrentRegister() const { return mCurrentOffset / ComponentsPerRegister; } - size_t getCurrentElement() const { return mCurrentOffset % ComponentsPerRegister; } virtual void enterAggregateType() = 0; virtual void exitAggregateType() = 0; diff --git a/gfx/angle/src/compiler/translator/blocklayoutHLSL.cpp b/gfx/angle/src/compiler/translator/blocklayoutHLSL.cpp index 2472ca8754..43119248eb 100644 --- a/gfx/angle/src/compiler/translator/blocklayoutHLSL.cpp +++ b/gfx/angle/src/compiler/translator/blocklayoutHLSL.cpp @@ -113,9 +113,14 @@ HLSLBlockEncoder::HLSLBlockEncoderStrategy HLSLBlockEncoder::GetStrategyFor(ShSh { switch (outputType) { - case SH_HLSL9_OUTPUT: return ENCODE_LOOSE; - case SH_HLSL11_OUTPUT: return ENCODE_PACKED; - default: UNREACHABLE(); return ENCODE_PACKED; + case SH_HLSL_3_0_OUTPUT: + return ENCODE_LOOSE; + case SH_HLSL_4_1_OUTPUT: + case SH_HLSL_4_0_FL9_3_OUTPUT: + return ENCODE_PACKED; + default: + UNREACHABLE(); + return ENCODE_PACKED; } } diff --git a/gfx/angle/src/compiler/translator/depgraph/DependencyGraph.h b/gfx/angle/src/compiler/translator/depgraph/DependencyGraph.h index 06fd6fb964..2f7f7b9ab8 100644 --- a/gfx/angle/src/compiler/translator/depgraph/DependencyGraph.h +++ b/gfx/angle/src/compiler/translator/depgraph/DependencyGraph.h @@ -142,27 +142,11 @@ class TDependencyGraph { public: TDependencyGraph(TIntermNode* intermNode); ~TDependencyGraph(); - TGraphNodeVector::const_iterator begin() const { return mAllNodes.begin(); } - TGraphNodeVector::const_iterator end() const { return mAllNodes.end(); } - - TGraphSymbolVector::const_iterator beginSamplerSymbols() const + const TGraphNodeVector &allNodes() const { return mAllNodes; } + const TGraphSymbolVector &samplerSymbols() const { return mSamplerSymbols; } + const TFunctionCallVector &userDefinedFunctionCalls() const { - return mSamplerSymbols.begin(); - } - - TGraphSymbolVector::const_iterator endSamplerSymbols() const - { - return mSamplerSymbols.end(); - } - - TFunctionCallVector::const_iterator beginUserDefinedFunctionCalls() const - { - return mUserDefinedFunctionCalls.begin(); - } - - TFunctionCallVector::const_iterator endUserDefinedFunctionCalls() const - { - return mUserDefinedFunctionCalls.end(); + return mUserDefinedFunctionCalls; } TGraphArgument* createArgument(TIntermAggregate* intermFunctionCall, int argumentNumber); diff --git a/gfx/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp b/gfx/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp index e226333545..32a2f30141 100644 --- a/gfx/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp +++ b/gfx/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp @@ -54,9 +54,8 @@ void TDependencyGraphOutput::outputAllSpanningTrees(TDependencyGraph& graph) { mSink << "\n"; - for (TGraphNodeVector::const_iterator iter = graph.begin(); iter != graph.end(); ++iter) + for (auto symbol : graph.allNodes()) { - TGraphNode* symbol = *iter; mSink << "--- Dependency graph spanning tree ---\n"; clearVisited(); symbol->traverse(this); diff --git a/gfx/angle/src/compiler/translator/generate_parser.sh b/gfx/angle/src/compiler/translator/generate_parser.sh index e4d88b2e84..61eecce735 100755 --- a/gfx/angle/src/compiler/translator/generate_parser.sh +++ b/gfx/angle/src/compiler/translator/generate_parser.sh @@ -7,22 +7,23 @@ run_flex() { -input_file=$script_dir/$1.l -output_source=$script_dir/$1_lex.cpp +input_file=./$1.l +output_source=./$1_lex.cpp flex --noline --nounistd --outfile=$output_source $input_file } run_bison() { -input_file=$script_dir/$1.y -output_header=$script_dir/$1_tab.h -output_source=$script_dir/$1_tab.cpp +input_file=./$1.y +output_header=./$1_tab.h +output_source=./$1_tab.cpp bison --no-lines --skeleton=yacc.c --defines=$output_header --output=$output_source $input_file } script_dir=$(dirname $0) # Generate Parser +cd $script_dir run_flex glslang run_bison glslang patch --silent --forward < 64bit-lexer-safety.patch diff --git a/gfx/angle/src/compiler/translator/glslang.y b/gfx/angle/src/compiler/translator/glslang.y index 13797bd579..aba2706311 100644 --- a/gfx/angle/src/compiler/translator/glslang.y +++ b/gfx/angle/src/compiler/translator/glslang.y @@ -178,10 +178,10 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons %type translation_unit function_definition %type statement simple_statement -%type statement_list compound_statement +%type statement_list compound_statement compound_statement_no_new_scope %type declaration_statement selection_statement expression_statement %type declaration external_declaration -%type for_init_statement compound_statement_no_new_scope +%type for_init_statement %type selection_rest_statement for_rest_statement %type switch_statement %type case_label @@ -580,33 +580,8 @@ enter_struct ; declaration - : function_prototype SEMICOLON { - TFunction &function = *($1.function); - - TIntermAggregate *prototype = new TIntermAggregate; - prototype->setType(function.getReturnType()); - prototype->setName(function.getMangledName()); - prototype->setFunctionId(function.getUniqueId()); - - for (size_t i = 0; i < function.getParamCount(); i++) - { - const TConstParameter ¶m = function.getParam(i); - if (param.name != 0) - { - TVariable variable(param.name, *param.type); - - prototype = context->intermediate.growAggregate(prototype, context->intermediate.addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), @1), @1); - } - else - { - prototype = context->intermediate.growAggregate(prototype, context->intermediate.addSymbol(0, "", *param.type, @1), @1); - } - } - - prototype->setOp(EOpPrototype); - $$ = prototype; - - context->symbolTable.pop(); + : function_prototype SEMICOLON { + $$ = context->addFunctionPrototypeDeclaration(*($1.function), @1); } | init_declarator_list SEMICOLON { TIntermAggregate *aggNode = $1.intermAggregate; @@ -1599,20 +1574,7 @@ function_definition context->parseFunctionPrototype(@1, $1.function, &$1.intermAggregate); } compound_statement_no_new_scope { - //?? Check that all paths return a value if return type != void ? - // May be best done as post process phase on intermediate code - if (context->getCurrentFunctionType()->getBasicType() != EbtVoid && !context->getFunctionReturnsValue()) { - context->error(@1, "function does not return a value:", "", $1.function->getName().c_str()); - context->recover(); - } - - $$ = context->intermediate.growAggregate($1.intermAggregate, $3, @$); - context->intermediate.setAggregateOperator($$, EOpFunction, @1); - $$->getAsAggregate()->setName($1.function->getMangledName().c_str()); - $$->getAsAggregate()->setType($1.function->getReturnType()); - $$->getAsAggregate()->setFunctionId($1.function->getUniqueId()); - - context->symbolTable.pop(); + $$ = context->addFunctionDefinition(*($1.function), $1.intermAggregate, $3, @1); } ; diff --git a/gfx/angle/src/compiler/translator/glslang_tab.cpp b/gfx/angle/src/compiler/translator/glslang_tab.cpp index 3fdd8b8211..8b4fbd2a23 100644 --- a/gfx/angle/src/compiler/translator/glslang_tab.cpp +++ b/gfx/angle/src/compiler/translator/glslang_tab.cpp @@ -702,26 +702,26 @@ static const yytype_uint16 yyrline[] = 440, 443, 446, 449, 455, 456, 459, 465, 466, 473, 474, 481, 482, 489, 490, 496, 497, 503, 504, 510, 511, 517, 518, 526, 527, 528, 529, 533, 534, 535, - 539, 543, 547, 551, 558, 561, 567, 575, 583, 611, - 617, 628, 632, 636, 640, 647, 653, 656, 663, 671, - 692, 719, 729, 757, 762, 772, 777, 787, 790, 793, - 796, 802, 809, 812, 816, 820, 825, 830, 837, 841, - 845, 849, 854, 859, 863, 870, 880, 886, 889, 895, - 901, 908, 917, 927, 935, 938, 945, 949, 953, 958, - 966, 969, 973, 977, 986, 995, 1003, 1013, 1025, 1028, - 1031, 1037, 1044, 1047, 1053, 1056, 1059, 1065, 1068, 1073, - 1088, 1092, 1096, 1100, 1104, 1108, 1113, 1118, 1123, 1128, - 1133, 1138, 1143, 1148, 1153, 1158, 1163, 1168, 1173, 1178, - 1183, 1188, 1193, 1198, 1203, 1208, 1213, 1217, 1221, 1225, - 1229, 1233, 1237, 1241, 1245, 1249, 1253, 1257, 1261, 1265, - 1269, 1273, 1281, 1289, 1293, 1306, 1306, 1309, 1309, 1315, - 1318, 1334, 1337, 1346, 1350, 1356, 1363, 1378, 1382, 1386, - 1387, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1403, 1404, - 1404, 1404, 1414, 1415, 1419, 1419, 1420, 1420, 1425, 1428, - 1438, 1441, 1447, 1448, 1452, 1460, 1464, 1471, 1471, 1478, - 1481, 1488, 1493, 1508, 1508, 1513, 1513, 1520, 1520, 1528, - 1531, 1537, 1540, 1546, 1550, 1557, 1560, 1563, 1566, 1569, - 1578, 1582, 1589, 1592, 1598, 1598 + 539, 543, 547, 551, 558, 561, 567, 575, 583, 586, + 592, 603, 607, 611, 615, 622, 628, 631, 638, 646, + 667, 694, 704, 732, 737, 747, 752, 762, 765, 768, + 771, 777, 784, 787, 791, 795, 800, 805, 812, 816, + 820, 824, 829, 834, 838, 845, 855, 861, 864, 870, + 876, 883, 892, 902, 910, 913, 920, 924, 928, 933, + 941, 944, 948, 952, 961, 970, 978, 988, 1000, 1003, + 1006, 1012, 1019, 1022, 1028, 1031, 1034, 1040, 1043, 1048, + 1063, 1067, 1071, 1075, 1079, 1083, 1088, 1093, 1098, 1103, + 1108, 1113, 1118, 1123, 1128, 1133, 1138, 1143, 1148, 1153, + 1158, 1163, 1168, 1173, 1178, 1183, 1188, 1192, 1196, 1200, + 1204, 1208, 1212, 1216, 1220, 1224, 1228, 1232, 1236, 1240, + 1244, 1248, 1256, 1264, 1268, 1281, 1281, 1284, 1284, 1290, + 1293, 1309, 1312, 1321, 1325, 1331, 1338, 1353, 1357, 1361, + 1362, 1368, 1369, 1370, 1371, 1372, 1373, 1374, 1378, 1379, + 1379, 1379, 1389, 1390, 1394, 1394, 1395, 1395, 1400, 1403, + 1413, 1416, 1422, 1423, 1427, 1435, 1439, 1446, 1446, 1453, + 1456, 1463, 1468, 1483, 1483, 1488, 1488, 1495, 1495, 1503, + 1506, 1512, 1515, 1521, 1525, 1532, 1535, 1538, 1541, 1544, + 1553, 1557, 1564, 1567, 1573, 1573 }; #endif @@ -3048,32 +3048,7 @@ yyreduce: case 88: { - TFunction &function = *((yyvsp[-1].interm).function); - - TIntermAggregate *prototype = new TIntermAggregate; - prototype->setType(function.getReturnType()); - prototype->setName(function.getMangledName()); - prototype->setFunctionId(function.getUniqueId()); - - for (size_t i = 0; i < function.getParamCount(); i++) - { - const TConstParameter ¶m = function.getParam(i); - if (param.name != 0) - { - TVariable variable(param.name, *param.type); - - prototype = context->intermediate.growAggregate(prototype, context->intermediate.addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), (yylsp[-1])), (yylsp[-1])); - } - else - { - prototype = context->intermediate.growAggregate(prototype, context->intermediate.addSymbol(0, "", *param.type, (yylsp[-1])), (yylsp[-1])); - } - } - - prototype->setOp(EOpPrototype); - (yyval.interm.intermNode) = prototype; - - context->symbolTable.pop(); + (yyval.interm.intermNode) = context->addFunctionPrototypeDeclaration(*((yyvsp[-1].interm).function), (yylsp[-1])); } break; @@ -4488,7 +4463,7 @@ yyreduce: case 232: - { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermAggregate); } break; @@ -4506,7 +4481,7 @@ yyreduce: case 235: - { context->symbolTable.pop(); (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } + { context->symbolTable.pop(); (yyval.interm.intermNode) = (yyvsp[0].interm.intermAggregate); } break; @@ -4525,7 +4500,7 @@ yyreduce: case 238: { - (yyval.interm.intermNode) = 0; + (yyval.interm.intermAggregate) = 0; } break; @@ -4537,7 +4512,7 @@ yyreduce: (yyvsp[-1].interm.intermAggregate)->setOp(EOpSequence); (yyvsp[-1].interm.intermAggregate)->setLine((yyloc)); } - (yyval.interm.intermNode) = (yyvsp[-1].interm.intermAggregate); + (yyval.interm.intermAggregate) = (yyvsp[-1].interm.intermAggregate); } break; @@ -4842,20 +4817,7 @@ yyreduce: case 275: { - //?? Check that all paths return a value if return type != void ? - // May be best done as post process phase on intermediate code - if (context->getCurrentFunctionType()->getBasicType() != EbtVoid && !context->getFunctionReturnsValue()) { - context->error((yylsp[-2]), "function does not return a value:", "", (yyvsp[-2].interm).function->getName().c_str()); - context->recover(); - } - - (yyval.interm.intermNode) = context->intermediate.growAggregate((yyvsp[-2].interm).intermAggregate, (yyvsp[0].interm.intermNode), (yyloc)); - context->intermediate.setAggregateOperator((yyval.interm.intermNode), EOpFunction, (yylsp[-2])); - (yyval.interm.intermNode)->getAsAggregate()->setName((yyvsp[-2].interm).function->getMangledName().c_str()); - (yyval.interm.intermNode)->getAsAggregate()->setType((yyvsp[-2].interm).function->getReturnType()); - (yyval.interm.intermNode)->getAsAggregate()->setFunctionId((yyvsp[-2].interm).function->getUniqueId()); - - context->symbolTable.pop(); + (yyval.interm.intermNode) = context->addFunctionDefinition(*((yyvsp[-2].interm).function), (yyvsp[-2].interm).intermAggregate, (yyvsp[0].interm.intermAggregate), (yylsp[-2])); } break; diff --git a/gfx/angle/src/compiler/translator/intermOut.cpp b/gfx/angle/src/compiler/translator/intermOut.cpp index 82384745de..6dca547f08 100644 --- a/gfx/angle/src/compiler/translator/intermOut.cpp +++ b/gfx/angle/src/compiler/translator/intermOut.cpp @@ -65,28 +65,6 @@ void OutputTreeText(TInfoSinkBase &sink, TIntermNode *node, const int depth) } // namespace anonymous - -TString TType::getCompleteString() const -{ - TStringStream stream; - - if (invariant) - stream << "invariant "; - if (qualifier != EvqTemporary && qualifier != EvqGlobal) - stream << getQualifierString() << " "; - if (precision != EbpUndefined) - stream << getPrecisionString() << " "; - if (array) - stream << "array[" << getArraySize() << "] of "; - if (isMatrix()) - stream << getCols() << "X" << getRows() << " matrix of "; - else if (isVector()) - stream << getNominalSize() << "-component vector of "; - - stream << getBasicString(); - return stream.str(); -} - // // The rest of the file are the traversal functions. The last one // is the one that starts the traversal. diff --git a/gfx/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp b/gfx/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp index 48d44c72d1..790974a2bf 100644 --- a/gfx/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp +++ b/gfx/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp @@ -54,11 +54,8 @@ void RestrictFragmentShaderTiming::enforceRestrictions(const TDependencyGraph& g // Starting from each sampler, traverse the dependency graph and generate an error each time we // hit a node where sampler dependent values are not allowed. - for (TGraphSymbolVector::const_iterator iter = graph.beginSamplerSymbols(); - iter != graph.endSamplerSymbols(); - ++iter) + for (auto samplerSymbol : graph.samplerSymbols()) { - TGraphSymbol* samplerSymbol = *iter; clearVisited(); samplerSymbol->traverse(this); } @@ -66,11 +63,8 @@ void RestrictFragmentShaderTiming::enforceRestrictions(const TDependencyGraph& g void RestrictFragmentShaderTiming::validateUserDefinedFunctionCallUsage(const TDependencyGraph& graph) { - for (TFunctionCallVector::const_iterator iter = graph.beginUserDefinedFunctionCalls(); - iter != graph.endUserDefinedFunctionCalls(); - ++iter) + for (const auto* functionCall : graph.userDefinedFunctionCalls()) { - TGraphFunctionCall* functionCall = *iter; beginError(functionCall->getIntermFunctionCall()); mSink << "A call to a user defined function is not permitted.\n"; } diff --git a/gfx/angle/src/libANGLE/BinaryStream.h b/gfx/angle/src/libANGLE/BinaryStream.h index 1712261790..3e6ccc7446 100644 --- a/gfx/angle/src/libANGLE/BinaryStream.h +++ b/gfx/angle/src/libANGLE/BinaryStream.h @@ -92,7 +92,7 @@ class BinaryInputStream : angle::NonCopyable return; } - if (mOffset + length > mLength) + if (!rx::IsUnsignedAdditionSafe(mOffset, length) || mOffset + length > mLength) { mError = true; return; @@ -104,7 +104,7 @@ class BinaryInputStream : angle::NonCopyable void skip(size_t length) { - if (mOffset + length > mLength) + if (!rx::IsUnsignedAdditionSafe(mOffset, length) || mOffset + length > mLength) { mError = true; return; @@ -144,9 +144,15 @@ class BinaryInputStream : angle::NonCopyable { StaticAssertIsFundamental(); + if (!rx::IsUnsignedMultiplicationSafe(num, sizeof(T))) + { + mError = true; + return; + } + size_t length = num * sizeof(T); - if (mOffset + length > mLength) + if (!rx::IsUnsignedAdditionSafe(mOffset, length) || mOffset + length > mLength) { mError = true; return; diff --git a/gfx/angle/src/libANGLE/BinaryStream_unittest.cpp b/gfx/angle/src/libANGLE/BinaryStream_unittest.cpp new file mode 100644 index 0000000000..963f489117 --- /dev/null +++ b/gfx/angle/src/libANGLE/BinaryStream_unittest.cpp @@ -0,0 +1,71 @@ +// +// Copyright (c) 2015 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. +// + +// BinaryStream_unittest.cpp: Unit tests of the binary stream classes. + +#include + +#include "libANGLE/BinaryStream.h" + +namespace angle +{ + +// Test that errors are properly generated for overflows. +TEST(BinaryInputStream, Overflow) +{ + const uint8_t goodValue = 2; + const uint8_t badValue = 255; + + const size_t dataSize = 1024; + const size_t slopSize = 1024; + + std::vector data(dataSize + slopSize); + std::fill(data.begin(), data.begin() + dataSize, goodValue); + std::fill(data.begin() + dataSize, data.end(), badValue); + + std::vector outputData(dataSize); + + auto checkDataIsSafe = [=](uint8_t item) + { + return item == goodValue; + }; + + { + // One large read + gl::BinaryInputStream stream(data.data(), dataSize); + stream.readBytes(outputData.data(), dataSize); + ASSERT_FALSE(stream.error()); + ASSERT_TRUE(std::all_of(outputData.begin(), outputData.end(), checkDataIsSafe)); + ASSERT_TRUE(stream.endOfStream()); + } + + { + // Two half-sized reads + gl::BinaryInputStream stream(data.data(), dataSize); + stream.readBytes(outputData.data(), dataSize / 2); + ASSERT_FALSE(stream.error()); + stream.readBytes(outputData.data() + dataSize / 2, dataSize / 2); + ASSERT_FALSE(stream.error()); + ASSERT_TRUE(std::all_of(outputData.begin(), outputData.end(), checkDataIsSafe)); + ASSERT_TRUE(stream.endOfStream()); + } + + { + // One large read that is too big + gl::BinaryInputStream stream(data.data(), dataSize); + stream.readBytes(outputData.data(), dataSize + 1); + ASSERT_TRUE(stream.error()); + } + + { + // Two reads, one that overflows the offset + gl::BinaryInputStream stream(data.data(), dataSize); + stream.readBytes(outputData.data(), dataSize - 1); + ASSERT_FALSE(stream.error()); + stream.readBytes(outputData.data(), std::numeric_limits::max() - dataSize - 2); + } +} +} diff --git a/gfx/angle/src/libANGLE/Buffer.cpp b/gfx/angle/src/libANGLE/Buffer.cpp index 59b2d5e417..589735c5a8 100644 --- a/gfx/angle/src/libANGLE/Buffer.cpp +++ b/gfx/angle/src/libANGLE/Buffer.cpp @@ -18,6 +18,7 @@ namespace gl Buffer::Buffer(rx::BufferImpl *impl, GLuint id) : RefCountObject(id), mBuffer(impl), + mLabel(), mUsage(GL_STATIC_DRAW), mSize(0), mAccessFlags(0), @@ -34,6 +35,16 @@ Buffer::~Buffer() SafeDelete(mBuffer); } +void Buffer::setLabel(const std::string &label) +{ + mLabel = label; +} + +const std::string &Buffer::getLabel() const +{ + return mLabel; +} + Error Buffer::bufferData(const void *data, GLsizeiptr size, GLenum usage) { gl::Error error = mBuffer->setData(data, size, usage); diff --git a/gfx/angle/src/libANGLE/Buffer.h b/gfx/angle/src/libANGLE/Buffer.h index be8d7f3f91..6c951ef586 100644 --- a/gfx/angle/src/libANGLE/Buffer.h +++ b/gfx/angle/src/libANGLE/Buffer.h @@ -12,6 +12,7 @@ #define LIBANGLE_BUFFER_H_ #include "common/angleutils.h" +#include "libANGLE/Debug.h" #include "libANGLE/Error.h" #include "libANGLE/IndexRangeCache.h" #include "libANGLE/RefCountObject.h" @@ -24,13 +25,15 @@ class BufferImpl; namespace gl { -class Buffer : public RefCountObject +class Buffer final : public RefCountObject, public LabeledObject { public: Buffer(rx::BufferImpl *impl, GLuint id); - virtual ~Buffer(); + void setLabel(const std::string &label) override; + const std::string &getLabel() const override; + Error bufferData(const void *data, GLsizeiptr size, GLenum usage); Error bufferSubData(const void *data, GLsizeiptr size, GLintptr offset); Error copyBufferSubData(Buffer* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size); @@ -61,6 +64,8 @@ class Buffer : public RefCountObject private: rx::BufferImpl *mBuffer; + std::string mLabel; + GLenum mUsage; GLint64 mSize; GLbitfield mAccessFlags; diff --git a/gfx/angle/src/libANGLE/Caps.cpp b/gfx/angle/src/libANGLE/Caps.cpp index 841be96959..1eb54a1589 100644 --- a/gfx/angle/src/libANGLE/Caps.cpp +++ b/gfx/angle/src/libANGLE/Caps.cpp @@ -102,6 +102,7 @@ Extensions::Extensions() pixelBufferObject(false), mapBuffer(false), mapBufferRange(false), + colorBufferHalfFloat(false), textureHalfFloat(false), textureHalfFloatLinear(false), textureFloat(false), @@ -114,6 +115,7 @@ Extensions::Extensions() textureCompressionASTCLDR(false), compressedETC1RGB8Texture(false), depthTextures(false), + depth32(false), textureStorage(false), textureNPOT(false), drawBuffers(false), @@ -122,6 +124,9 @@ Extensions::Extensions() occlusionQueryBoolean(false), fence(false), timerQuery(false), + disjointTimerQuery(false), + queryCounterBitsTimeElapsed(0), + queryCounterBitsTimestamp(0), robustness(false), blendMinMax(false), framebufferBlit(false), @@ -145,6 +150,13 @@ Extensions::Extensions() unpackSubimage(false), packSubimage(false), vertexArrayObject(false), + debug(false), + maxDebugMessageLength(0), + maxDebugLoggedMessages(0), + maxDebugGroupStackDepth(0), + maxLabelLength(0), + noError(false), + lossyETCDecode(false), colorBufferFloat(false) { } @@ -164,6 +176,7 @@ std::vector Extensions::getStrings() const InsertExtensionString("GL_NV_pixel_buffer_object", pixelBufferObject, &extensionStrings); InsertExtensionString("GL_OES_mapbuffer", mapBuffer, &extensionStrings); InsertExtensionString("GL_EXT_map_buffer_range", mapBufferRange, &extensionStrings); + InsertExtensionString("GL_EXT_color_buffer_half_float", colorBufferHalfFloat, &extensionStrings); InsertExtensionString("GL_OES_texture_half_float", textureHalfFloat, &extensionStrings); InsertExtensionString("GL_OES_texture_half_float_linear", textureHalfFloatLinear, &extensionStrings); InsertExtensionString("GL_OES_texture_float", textureFloat, &extensionStrings); @@ -177,6 +190,7 @@ std::vector Extensions::getStrings() const InsertExtensionString("GL_OES_compressed_ETC1_RGB8_texture", compressedETC1RGB8Texture, &extensionStrings); InsertExtensionString("GL_EXT_sRGB", sRGB, &extensionStrings); InsertExtensionString("GL_ANGLE_depth_texture", depthTextures, &extensionStrings); + InsertExtensionString("GL_OES_depth32", depth32, &extensionStrings); InsertExtensionString("GL_EXT_texture_storage", textureStorage, &extensionStrings); InsertExtensionString("GL_OES_texture_npot", textureNPOT, &extensionStrings); InsertExtensionString("GL_EXT_draw_buffers", drawBuffers, &extensionStrings); @@ -184,6 +198,7 @@ std::vector Extensions::getStrings() const InsertExtensionString("GL_EXT_occlusion_query_boolean", occlusionQueryBoolean, &extensionStrings); InsertExtensionString("GL_NV_fence", fence, &extensionStrings); InsertExtensionString("GL_ANGLE_timer_query", timerQuery, &extensionStrings); + InsertExtensionString("GL_EXT_disjoint_timer_query", disjointTimerQuery, &extensionStrings); InsertExtensionString("GL_EXT_robustness", robustness, &extensionStrings); InsertExtensionString("GL_EXT_blend_minmax", blendMinMax, &extensionStrings); InsertExtensionString("GL_ANGLE_framebuffer_blit", framebufferBlit, &extensionStrings); @@ -208,6 +223,11 @@ std::vector Extensions::getStrings() const InsertExtensionString("GL_NV_pack_subimage", packSubimage, &extensionStrings); InsertExtensionString("GL_EXT_color_buffer_float", colorBufferFloat, &extensionStrings); InsertExtensionString("GL_OES_vertex_array_object", vertexArrayObject, &extensionStrings); + InsertExtensionString("GL_KHR_debug", debug, &extensionStrings); + // TODO(jmadill): Enable this when complete. + //InsertExtensionString("GL_KHR_no_error", noError, &extensionStrings); + + InsertExtensionString("GL_ANGLE_lossy_etc_decode", lossyETCDecode, &extensionStrings); // clang-format on return extensionStrings; @@ -277,6 +297,18 @@ static bool DetermineBGRA8TextureSupport(const TextureCapsMap &textureCaps) return GetFormatSupport(textureCaps, requiredFormats, true, true, true); } +// Checks for GL_OES_color_buffer_half_float support +static bool DetermineColorBufferHalfFloatSupport(const TextureCapsMap &textureCaps) +{ + std::vector requiredFormats; + requiredFormats.push_back(GL_RGBA16F); + requiredFormats.push_back(GL_RGB16F); + requiredFormats.push_back(GL_RG16F); + requiredFormats.push_back(GL_R16F); + + return GetFormatSupport(textureCaps, requiredFormats, true, false, true); +} + // Checks for GL_OES_texture_half_float support static bool DetermineHalfFloatTextureSupport(const TextureCapsMap &textureCaps) { @@ -294,7 +326,8 @@ static bool DetermineHalfFloatTextureFilteringSupport(const TextureCapsMap &text requiredFormats.push_back(GL_RGB16F); requiredFormats.push_back(GL_RGBA16F); - return GetFormatSupport(textureCaps, requiredFormats, true, true, false); + return DetermineHalfFloatTextureSupport(textureCaps) && + GetFormatSupport(textureCaps, requiredFormats, true, true, false); } // Checks for GL_OES_texture_float support @@ -314,7 +347,8 @@ static bool DetermineFloatTextureFilteringSupport(const TextureCapsMap &textureC requiredFormats.push_back(GL_RGB32F); requiredFormats.push_back(GL_RGBA32F); - return GetFormatSupport(textureCaps, requiredFormats, true, true, false); + return DetermineFloatTextureSupport(textureCaps) && + GetFormatSupport(textureCaps, requiredFormats, true, true, false); } // Checks for GL_EXT_texture_rg support @@ -435,6 +469,15 @@ static bool DetermineDepthTextureSupport(const TextureCapsMap &textureCaps) return GetFormatSupport(textureCaps, requiredFormats, true, true, true); } +// Check for GL_OES_depth32 +static bool DetermineDepth32Support(const TextureCapsMap &textureCaps) +{ + std::vector requiredFormats; + requiredFormats.push_back(GL_DEPTH_COMPONENT32_OES); + + return GetFormatSupport(textureCaps, requiredFormats, false, false, true); +} + // Check for GL_EXT_color_buffer_float static bool DetermineColorBufferFloatSupport(const TextureCapsMap &textureCaps) { @@ -455,6 +498,7 @@ void Extensions::setTextureExtensionSupport(const TextureCapsMap &textureCaps) packedDepthStencil = DeterminePackedDepthStencilSupport(textureCaps); rgb8rgba8 = DetermineRGB8AndRGBA8TextureSupport(textureCaps); textureFormatBGRA8888 = DetermineBGRA8TextureSupport(textureCaps); + colorBufferHalfFloat = DetermineColorBufferHalfFloatSupport(textureCaps); textureHalfFloat = DetermineHalfFloatTextureSupport(textureCaps); textureHalfFloatLinear = DetermineHalfFloatTextureFilteringSupport(textureCaps); textureFloat = DetermineFloatTextureSupport(textureCaps); @@ -468,6 +512,7 @@ void Extensions::setTextureExtensionSupport(const TextureCapsMap &textureCaps) compressedETC1RGB8Texture = DetermineETC1RGB8TextureSupport(textureCaps); sRGB = DetermineSRGBTextureSupport(textureCaps); depthTextures = DetermineDepthTextureSupport(textureCaps); + depth32 = DetermineDepth32Support(textureCaps); colorBufferFloat = DetermineColorBufferFloatSupport(textureCaps); } @@ -576,6 +621,7 @@ DisplayExtensions::DisplayExtensions() querySurfacePointer(false), windowFixedSize(false), keyedMutex(false), + surfaceOrientation(false), postSubBuffer(false), createContext(false), deviceQuery(false), @@ -586,7 +632,10 @@ DisplayExtensions::DisplayExtensions() glTextureCubemapImage(false), glTexture3DImage(false), glRenderbufferImage(false), - getAllProcAddresses(false) + getAllProcAddresses(false), + flexibleSurfaceCompatibility(false), + directComposition(false), + createContextNoError(false) { } @@ -602,6 +651,8 @@ std::vector DisplayExtensions::getStrings() const InsertExtensionString("EGL_ANGLE_query_surface_pointer", querySurfacePointer, &extensionStrings); InsertExtensionString("EGL_ANGLE_window_fixed_size", windowFixedSize, &extensionStrings); InsertExtensionString("EGL_ANGLE_keyed_mutex", keyedMutex, &extensionStrings); + InsertExtensionString("EGL_ANGLE_surface_orientation", surfaceOrientation, &extensionStrings); + InsertExtensionString("EGL_ANGLE_direct_composition", directComposition, &extensionStrings); InsertExtensionString("EGL_NV_post_sub_buffer", postSubBuffer, &extensionStrings); InsertExtensionString("EGL_KHR_create_context", createContext, &extensionStrings); InsertExtensionString("EGL_EXT_device_query", deviceQuery, &extensionStrings); @@ -613,6 +664,9 @@ std::vector DisplayExtensions::getStrings() const InsertExtensionString("EGL_KHR_gl_texture_3D_image", glTexture3DImage, &extensionStrings); InsertExtensionString("EGL_KHR_gl_renderbuffer_image", glRenderbufferImage, &extensionStrings); InsertExtensionString("EGL_KHR_get_all_proc_addresses", getAllProcAddresses, &extensionStrings); + InsertExtensionString("EGL_ANGLE_flexible_surface_compatibility", flexibleSurfaceCompatibility, &extensionStrings); + // TODO(jmadill): Enable this when complete. + //InsertExtensionString("KHR_create_context_no_error", createContextNoError, &extensionStrings); // clang-format on return extensionStrings; @@ -636,9 +690,14 @@ std::vector DeviceExtensions::getStrings() const ClientExtensions::ClientExtensions() : clientExtensions(false), platformBase(false), + platformDevice(false), platformANGLE(false), platformANGLED3D(false), platformANGLEOpenGL(false), + deviceCreation(false), + deviceCreationD3D11(false), + x11Visual(false), + experimentalPresentPath(false), clientGetAllProcAddresses(false) { } @@ -651,9 +710,14 @@ std::vector ClientExtensions::getStrings() const // | Extension name | Supported flag | Output vector | InsertExtensionString("EGL_EXT_client_extensions", clientExtensions, &extensionStrings); InsertExtensionString("EGL_EXT_platform_base", platformBase, &extensionStrings); + InsertExtensionString("EGL_EXT_platform_device", platformDevice, &extensionStrings); InsertExtensionString("EGL_ANGLE_platform_angle", platformANGLE, &extensionStrings); InsertExtensionString("EGL_ANGLE_platform_angle_d3d", platformANGLED3D, &extensionStrings); InsertExtensionString("EGL_ANGLE_platform_angle_opengl", platformANGLEOpenGL, &extensionStrings); + InsertExtensionString("EGL_ANGLE_device_creation", deviceCreation, &extensionStrings); + InsertExtensionString("EGL_ANGLE_device_creation_d3d11", deviceCreationD3D11, &extensionStrings); + InsertExtensionString("EGL_ANGLE_x11_visual", x11Visual, &extensionStrings); + InsertExtensionString("EGL_ANGLE_experimental_present_path", experimentalPresentPath, &extensionStrings); InsertExtensionString("EGL_KHR_client_get_all_proc_addresses", clientGetAllProcAddresses, &extensionStrings); // clang-format on diff --git a/gfx/angle/src/libANGLE/Caps.h b/gfx/angle/src/libANGLE/Caps.h index 3e1c1d1cb2..d0e839a2ba 100644 --- a/gfx/angle/src/libANGLE/Caps.h +++ b/gfx/angle/src/libANGLE/Caps.h @@ -76,14 +76,16 @@ struct Extensions // GL_OES_packed_depth_stencil // GL_OES_rgb8_rgba8 // GL_EXT_texture_format_BGRA8888 + // GL_EXT_color_buffer_half_float, // GL_OES_texture_half_float, GL_OES_texture_half_float_linear // GL_OES_texture_float, GL_OES_texture_float_linear // GL_EXT_texture_rg - // GL_EXT_texture_compression_dxt1, GL_ANGLE_texture_compression_dxt3, GL_ANGLE_texture_compression_dxt5 + // GL_EXT_texture_compression_dxt1, GL_ANGLE_texture_compression_dxt3, + // GL_ANGLE_texture_compression_dxt5 // GL_KHR_texture_compression_astc_hdr, GL_KHR_texture_compression_astc_ldr // GL_OES_compressed_ETC1_RGB8_texture // GL_EXT_sRGB - // GL_ANGLE_depth_texture + // GL_ANGLE_depth_texture, GL_OES_depth32 // GL_EXT_color_buffer_float void setTextureExtensionSupport(const TextureCapsMap &textureCaps); @@ -116,6 +118,11 @@ struct Extensions bool mapBuffer; bool mapBufferRange; + // GL_EXT_color_buffer_half_float + // Together with GL_OES_texture_half_float in a GLES 2.0 context, implies that half-float + // textures are renderable. + bool colorBufferHalfFloat; + // GL_OES_texture_half_float and GL_OES_texture_half_float_linear // Implies that TextureCaps for GL_RGB16F, GL_RGBA16F, GL_ALPHA32F_EXT, GL_LUMINANCE32F_EXT and // GL_LUMINANCE_ALPHA32F_EXT exist @@ -158,6 +165,10 @@ struct Extensions // GL_ANGLE_depth_texture bool depthTextures; + // GL_OES_depth32 + // Allows DEPTH_COMPONENT32_OES as a valid Renderbuffer format. + bool depth32; + // GL_EXT_texture_storage bool textureStorage; @@ -180,6 +191,11 @@ struct Extensions // GL_ANGLE_timer_query bool timerQuery; + // GL_EXT_disjoint_timer_query + bool disjointTimerQuery; + GLuint queryCounterBitsTimeElapsed; + GLuint queryCounterBitsTimestamp; + // GL_EXT_robustness bool robustness; @@ -249,6 +265,19 @@ struct Extensions // GL_OES_vertex_array_object bool vertexArrayObject; + // GL_KHR_debug + bool debug; + GLuint maxDebugMessageLength; + GLuint maxDebugLoggedMessages; + GLuint maxDebugGroupStackDepth; + GLuint maxLabelLength; + + // KHR_no_error + bool noError; + + // GL_ANGLE_lossy_etc_decode + bool lossyETCDecode; + // ES3 Extension support // GL_EXT_color_buffer_float @@ -409,6 +438,9 @@ struct DisplayExtensions // EGL_ANGLE_keyed_mutex bool keyedMutex; + // EGL_ANGLE_surface_orientation + bool surfaceOrientation; + // EGL_NV_post_sub_buffer bool postSubBuffer; @@ -441,6 +473,15 @@ struct DisplayExtensions // EGL_KHR_get_all_proc_addresses bool getAllProcAddresses; + + // EGL_ANGLE_flexible_surface_compatibility + bool flexibleSurfaceCompatibility; + + // EGL_ANGLE_direct_composition + bool directComposition; + + // KHR_create_context_no_error + bool createContextNoError; }; struct DeviceExtensions @@ -467,6 +508,9 @@ struct ClientExtensions // EGL_EXT_platform_base bool platformBase; + // EGL_EXT_platform_device + bool platformDevice; + // EGL_ANGLE_platform_angle bool platformANGLE; @@ -476,6 +520,18 @@ struct ClientExtensions // EGL_ANGLE_platform_angle_opengl bool platformANGLEOpenGL; + // EGL_ANGLE_device_creation + bool deviceCreation; + + // EGL_ANGLE_device_creation_d3d11 + bool deviceCreationD3D11; + + // EGL_ANGLE_x11_visual + bool x11Visual; + + // EGL_ANGLE_experimental_present_path + bool experimentalPresentPath; + // EGL_KHR_client_get_all_proc_addresses bool clientGetAllProcAddresses; }; diff --git a/gfx/angle/src/libANGLE/Config.cpp b/gfx/angle/src/libANGLE/Config.cpp index c558b310e2..d511df3a69 100644 --- a/gfx/angle/src/libANGLE/Config.cpp +++ b/gfx/angle/src/libANGLE/Config.cpp @@ -57,7 +57,8 @@ Config::Config() transparentType(EGL_NONE), transparentRedValue(0), transparentGreenValue(0), - transparentBlueValue(0) + transparentBlueValue(0), + optimalOrientation(0) { } @@ -251,6 +252,9 @@ std::vector ConfigSet::filter(const AttributeMap &attributeMap) c case EGL_MAX_PBUFFER_WIDTH: match = config.maxPBufferWidth >= attributeValue; break; case EGL_MAX_PBUFFER_HEIGHT: match = config.maxPBufferHeight >= attributeValue; break; case EGL_MAX_PBUFFER_PIXELS: match = config.maxPBufferPixels >= attributeValue; break; + case EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE: + match = config.optimalOrientation == attributeValue; + break; default: UNREACHABLE(); } diff --git a/gfx/angle/src/libANGLE/Config.h b/gfx/angle/src/libANGLE/Config.h index aed8aedb1d..00f5673b59 100644 --- a/gfx/angle/src/libANGLE/Config.h +++ b/gfx/angle/src/libANGLE/Config.h @@ -64,6 +64,7 @@ struct Config EGLint transparentRedValue; // Transparent red value EGLint transparentGreenValue; // Transparent green value EGLint transparentBlueValue; // Transparent blue value + EGLint optimalOrientation; // Optimal window surface orientation }; class ConfigSet diff --git a/gfx/angle/src/libANGLE/Context.cpp b/gfx/angle/src/libANGLE/Context.cpp index 1a1bcd2187..26f2970068 100644 --- a/gfx/angle/src/libANGLE/Context.cpp +++ b/gfx/angle/src/libANGLE/Context.cpp @@ -36,6 +36,32 @@ namespace { +template +gl::Error GetQueryObjectParameter(gl::Context *context, GLuint id, GLenum pname, T *params) +{ + gl::Query *queryObject = context->getQuery(id, false, GL_NONE); + ASSERT(queryObject != nullptr); + + switch (pname) + { + case GL_QUERY_RESULT_EXT: + return queryObject->getResult(params); + case GL_QUERY_RESULT_AVAILABLE_EXT: + { + bool available; + gl::Error error = queryObject->isResultAvailable(&available); + if (!error.isError()) + { + *params = static_cast(available ? GL_TRUE : GL_FALSE); + } + return error; + } + default: + UNREACHABLE(); + return gl::Error(GL_INVALID_OPERATION, "Unreachable Error"); + } +} + void MarkTransformFeedbackBufferUsage(gl::TransformFeedback *transformFeedback) { if (transformFeedback && transformFeedback->isActive() && !transformFeedback->isPaused()) @@ -52,36 +78,79 @@ void MarkTransformFeedbackBufferUsage(gl::TransformFeedback *transformFeedback) } } } + +// Attribute map queries. +EGLint GetClientVersion(const egl::AttributeMap &attribs) +{ + return attribs.get(EGL_CONTEXT_CLIENT_VERSION, 1); +} + +GLenum GetResetStrategy(const egl::AttributeMap &attribs) +{ + EGLenum attrib = attribs.get(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT, + EGL_NO_RESET_NOTIFICATION_EXT); + switch (attrib) + { + case EGL_NO_RESET_NOTIFICATION: + return GL_NO_RESET_NOTIFICATION_EXT; + case EGL_LOSE_CONTEXT_ON_RESET: + return GL_LOSE_CONTEXT_ON_RESET_EXT; + default: + UNREACHABLE(); + return GL_NONE; + } +} + +bool GetRobustAccess(const egl::AttributeMap &attribs) +{ + return (attribs.get(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT, EGL_FALSE) == EGL_TRUE); +} + +bool GetDebug(const egl::AttributeMap &attribs) +{ + return (attribs.get(EGL_CONTEXT_OPENGL_DEBUG, EGL_FALSE) == EGL_TRUE); +} + +bool GetNoError(const egl::AttributeMap &attribs) +{ + return (attribs.get(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, EGL_FALSE) == EGL_TRUE); +} + } // anonymous namespace namespace gl { Context::Context(const egl::Config *config, - int clientVersion, const Context *shareContext, rx::Renderer *renderer, - bool notifyResets, - bool robustAccess) - : ValidationContext(clientVersion, + const egl::AttributeMap &attribs) + : ValidationContext(GetClientVersion(attribs), mState, mCaps, mTextureCaps, mExtensions, nullptr, - mLimitations), + mLimitations, + GetNoError(attribs)), + mCompiler(nullptr), mRenderer(renderer), + mClientVersion(GetClientVersion(attribs)), mConfig(config), - mCurrentSurface(nullptr) + mClientType(EGL_OPENGL_ES_API), + mHasBeenCurrent(false), + mContextLost(false), + mResetStatus(GL_NO_ERROR), + mResetStrategy(GetResetStrategy(attribs)), + mRobustAccess(GetRobustAccess(attribs)), + mCurrentSurface(nullptr), + mResourceManager(nullptr) { - ASSERT(robustAccess == false); // Unimplemented + ASSERT(!mRobustAccess); // Unimplemented - initCaps(clientVersion); - mState.initialize(mCaps, clientVersion); + initCaps(mClientVersion); - mClientVersion = clientVersion; - - mClientType = EGL_OPENGL_ES_API; + mState.initialize(mCaps, mExtensions, mClientVersion, GetDebug(attribs)); mFenceNVHandleAllocator.setBaseHandle(0); @@ -144,17 +213,9 @@ Context::Context(const egl::Config *config, // In the initial state, a default transform feedback object is bound and treated as // a transform feedback object with a name of zero. That object is bound any time // BindTransformFeedback is called with id of zero - mTransformFeedbackZero.set( - new TransformFeedback(mRenderer->createTransformFeedback(), 0, mCaps)); bindTransformFeedback(0); } - mHasBeenCurrent = false; - mContextLost = false; - mResetStatus = GL_NO_ERROR; - mResetStrategy = (notifyResets ? GL_LOSE_CONTEXT_ON_RESET_EXT : GL_NO_RESET_NOTIFICATION_EXT); - mRobustAccess = robustAccess; - mCompiler = new Compiler(mRenderer, getData()); } @@ -189,10 +250,12 @@ Context::~Context() SafeDelete(vertexArray.second); } - mTransformFeedbackZero.set(NULL); for (auto transformFeedback : mTransformFeedbackMap) { - transformFeedback.second->release(); + if (transformFeedback.second != nullptr) + { + transformFeedback.second->release(); + } } for (auto &zeroTexture : mZeroTextures) @@ -253,6 +316,9 @@ void Context::makeCurrent(egl::Surface *surface) } mFramebufferMap[0] = newDefault; } + + // Notify the renderer of a context switch + mRenderer->onMakeCurrent(getData()); } void Context::releaseSurface() @@ -324,14 +390,9 @@ GLsync Context::createFenceSync() GLuint Context::createVertexArray() { - GLuint handle = mVertexArrayHandleAllocator.allocate(); - - // Although the spec states VAO state is not initialized until the object is bound, - // we create it immediately. The resulting behaviour is transparent to the application, - // since it's not currently possible to access the state until the object is bound. - VertexArray *vertexArray = new VertexArray(mRenderer, handle, MAX_VERTEX_ATTRIBS); - mVertexArrayMap[handle] = vertexArray; - return handle; + GLuint vertexArray = mVertexArrayHandleAllocator.allocate(); + mVertexArrayMap[vertexArray] = nullptr; + return vertexArray; } GLuint Context::createSampler() @@ -341,11 +402,9 @@ GLuint Context::createSampler() GLuint Context::createTransformFeedback() { - GLuint handle = mTransformFeedbackAllocator.allocate(); - TransformFeedback *transformFeedback = new TransformFeedback(mRenderer->createTransformFeedback(), handle, mCaps); - transformFeedback->addRef(); - mTransformFeedbackMap[handle] = transformFeedback; - return handle; + GLuint transformFeedback = mTransformFeedbackAllocator.allocate(); + mTransformFeedbackMap[transformFeedback] = nullptr; + return transformFeedback; } // Returns an unused framebuffer name @@ -428,15 +487,18 @@ void Context::deleteFenceSync(GLsync fenceSync) void Context::deleteVertexArray(GLuint vertexArray) { - auto vertexArrayObject = mVertexArrayMap.find(vertexArray); - - if (vertexArrayObject != mVertexArrayMap.end()) + auto iter = mVertexArrayMap.find(vertexArray); + if (iter != mVertexArrayMap.end()) { - detachVertexArray(vertexArray); + VertexArray *vertexArrayObject = iter->second; + if (vertexArrayObject != nullptr) + { + detachVertexArray(vertexArray); + delete vertexArrayObject; + } - mVertexArrayHandleAllocator.release(vertexArrayObject->first); - delete vertexArrayObject->second; - mVertexArrayMap.erase(vertexArrayObject); + mVertexArrayMap.erase(iter); + mVertexArrayHandleAllocator.release(vertexArray); } } @@ -455,10 +517,15 @@ void Context::deleteTransformFeedback(GLuint transformFeedback) auto iter = mTransformFeedbackMap.find(transformFeedback); if (iter != mTransformFeedbackMap.end()) { - detachTransformFeedback(transformFeedback); - mTransformFeedbackAllocator.release(transformFeedback); - iter->second->release(); + TransformFeedback *transformFeedbackObject = iter->second; + if (transformFeedbackObject != nullptr) + { + detachTransformFeedback(transformFeedback); + transformFeedbackObject->release(); + } + mTransformFeedbackMap.erase(iter); + mTransformFeedbackAllocator.release(transformFeedback); } } @@ -502,7 +569,7 @@ void Context::deleteQuery(GLuint query) } } -Buffer *Context::getBuffer(GLuint handle) +Buffer *Context::getBuffer(GLuint handle) const { return mResourceManager->getBuffer(handle); } @@ -522,7 +589,7 @@ Texture *Context::getTexture(GLuint handle) const return mResourceManager->getTexture(handle); } -Renderbuffer *Context::getRenderbuffer(GLuint handle) +Renderbuffer *Context::getRenderbuffer(GLuint handle) const { return mResourceManager->getRenderbuffer(handle); } @@ -535,15 +602,7 @@ FenceSync *Context::getFenceSync(GLsync handle) const VertexArray *Context::getVertexArray(GLuint handle) const { auto vertexArray = mVertexArrayMap.find(handle); - - if (vertexArray == mVertexArrayMap.end()) - { - return NULL; - } - else - { - return vertexArray->second; - } + return (vertexArray != mVertexArrayMap.end()) ? vertexArray->second : nullptr; } Sampler *Context::getSampler(GLuint handle) const @@ -553,17 +612,45 @@ Sampler *Context::getSampler(GLuint handle) const TransformFeedback *Context::getTransformFeedback(GLuint handle) const { - if (handle == 0) + auto iter = mTransformFeedbackMap.find(handle); + return (iter != mTransformFeedbackMap.end()) ? iter->second : nullptr; +} + +LabeledObject *Context::getLabeledObject(GLenum identifier, GLuint name) const +{ + switch (identifier) { - return mTransformFeedbackZero.get(); - } - else - { - TransformFeedbackMap::const_iterator iter = mTransformFeedbackMap.find(handle); - return (iter != mTransformFeedbackMap.end()) ? iter->second : NULL; + case GL_BUFFER: + return getBuffer(name); + case GL_SHADER: + return getShader(name); + case GL_PROGRAM: + return getProgram(name); + case GL_VERTEX_ARRAY: + return getVertexArray(name); + case GL_QUERY: + return getQuery(name); + case GL_TRANSFORM_FEEDBACK: + return getTransformFeedback(name); + case GL_SAMPLER: + return getSampler(name); + case GL_TEXTURE: + return getTexture(name); + case GL_RENDERBUFFER: + return getRenderbuffer(name); + case GL_FRAMEBUFFER: + return getFramebuffer(name); + default: + UNREACHABLE(); + return nullptr; } } +LabeledObject *Context::getLabeledObjectFromPtr(const void *ptr) const +{ + return getFenceSync(reinterpret_cast(const_cast(ptr))); +} + bool Context::isSampler(GLuint samplerName) const { return mResourceManager->isSampler(samplerName); @@ -602,24 +689,16 @@ void Context::bindTexture(GLenum target, GLuint handle) mState.setSamplerTexture(target, texture); } -void Context::bindReadFramebuffer(GLuint framebuffer) +void Context::bindReadFramebuffer(GLuint framebufferHandle) { - if (!getFramebuffer(framebuffer)) - { - mFramebufferMap[framebuffer] = new Framebuffer(mCaps, mRenderer, framebuffer); - } - - mState.setReadFramebufferBinding(getFramebuffer(framebuffer)); + Framebuffer *framebuffer = checkFramebufferAllocation(framebufferHandle); + mState.setReadFramebufferBinding(framebuffer); } -void Context::bindDrawFramebuffer(GLuint framebuffer) +void Context::bindDrawFramebuffer(GLuint framebufferHandle) { - if (!getFramebuffer(framebuffer)) - { - mFramebufferMap[framebuffer] = new Framebuffer(mCaps, mRenderer, framebuffer); - } - - mState.setDrawFramebufferBinding(getFramebuffer(framebuffer)); + Framebuffer *framebuffer = checkFramebufferAllocation(framebufferHandle); + mState.setDrawFramebufferBinding(framebuffer); } void Context::bindRenderbuffer(GLuint renderbuffer) @@ -631,11 +710,7 @@ void Context::bindRenderbuffer(GLuint renderbuffer) void Context::bindVertexArray(GLuint vertexArray) { - if (!getVertexArray(vertexArray)) - { - VertexArray *vertexArrayObject = new VertexArray(mRenderer, vertexArray, MAX_VERTEX_ATTRIBS); - mVertexArrayMap[vertexArray] = vertexArrayObject; - } + checkVertexArrayAllocation(vertexArray); mState.setVertexArrayBinding(getVertexArray(vertexArray)); } @@ -711,6 +786,8 @@ void Context::useProgram(GLuint program) void Context::bindTransformFeedback(GLuint transformFeedback) { + checkTransformFeedbackAllocation(transformFeedback); + mState.setTransformFeedbackBinding(getTransformFeedback(transformFeedback)); } @@ -745,18 +822,68 @@ Error Context::endQuery(GLenum target) return error; } +Error Context::queryCounter(GLuint id, GLenum target) +{ + ASSERT(target == GL_TIMESTAMP_EXT); + + Query *queryObject = getQuery(id, true, target); + ASSERT(queryObject); + + return queryObject->queryCounter(); +} + +void Context::getQueryiv(GLenum target, GLenum pname, GLint *params) +{ + switch (pname) + { + case GL_CURRENT_QUERY_EXT: + params[0] = getState().getActiveQueryId(target); + break; + case GL_QUERY_COUNTER_BITS_EXT: + switch (target) + { + case GL_TIME_ELAPSED_EXT: + params[0] = getExtensions().queryCounterBitsTimeElapsed; + break; + case GL_TIMESTAMP_EXT: + params[0] = getExtensions().queryCounterBitsTimestamp; + break; + default: + UNREACHABLE(); + params[0] = 0; + break; + } + break; + default: + UNREACHABLE(); + return; + } +} + +Error Context::getQueryObjectiv(GLuint id, GLenum pname, GLint *params) +{ + return GetQueryObjectParameter(this, id, pname, params); +} + +Error Context::getQueryObjectuiv(GLuint id, GLenum pname, GLuint *params) +{ + return GetQueryObjectParameter(this, id, pname, params); +} + +Error Context::getQueryObjecti64v(GLuint id, GLenum pname, GLint64 *params) +{ + return GetQueryObjectParameter(this, id, pname, params); +} + +Error Context::getQueryObjectui64v(GLuint id, GLenum pname, GLuint64 *params) +{ + return GetQueryObjectParameter(this, id, pname, params); +} + Framebuffer *Context::getFramebuffer(unsigned int handle) const { - FramebufferMap::const_iterator framebuffer = mFramebufferMap.find(handle); - - if (framebuffer == mFramebufferMap.end()) - { - return NULL; - } - else - { - return framebuffer->second; - } + auto framebufferIt = mFramebufferMap.find(handle); + return ((framebufferIt == mFramebufferMap.end()) ? nullptr : framebufferIt->second); } FenceNV *Context::getFenceNV(unsigned int handle) @@ -792,11 +919,16 @@ Query *Context::getQuery(unsigned int handle, bool create, GLenum type) } } +Query *Context::getQuery(GLuint handle) const +{ + auto iter = mQueryMap.find(handle); + return (iter != mQueryMap.end()) ? iter->second : nullptr; +} + Texture *Context::getTargetTexture(GLenum target) const { ASSERT(ValidTextureTarget(this, target)); - - return getSamplerTexture(mState.getActiveSampler(), target); + return mState.getTargetTexture(target); } Texture *Context::getSamplerTexture(unsigned int sampler, GLenum type) const @@ -921,6 +1053,26 @@ void Context::getIntegerv(GLenum pname, GLint *params) case GL_NUM_EXTENSIONS: *params = static_cast(mExtensionStrings.size()); break; + + // GL_KHR_debug + case GL_MAX_DEBUG_MESSAGE_LENGTH: + *params = mExtensions.maxDebugMessageLength; + break; + case GL_MAX_DEBUG_LOGGED_MESSAGES: + *params = mExtensions.maxDebugLoggedMessages; + break; + case GL_MAX_DEBUG_GROUP_STACK_DEPTH: + *params = mExtensions.maxDebugGroupStackDepth; + break; + case GL_MAX_LABEL_LENGTH: + *params = mExtensions.maxLabelLength; + break; + + // GL_EXT_disjoint_timer_query + case GL_GPU_DISJOINT_EXT: + *params = mRenderer->getGPUDisjoint(); + break; + default: mState.getIntegerv(getData(), pname, params); break; @@ -948,12 +1100,22 @@ void Context::getInteger64v(GLenum pname, GLint64 *params) case GL_MAX_SERVER_WAIT_TIMEOUT: *params = mCaps.maxServerWaitTimeout; break; + + // GL_EXT_disjoint_timer_query + case GL_TIMESTAMP_EXT: + *params = mRenderer->getTimestamp(); + break; default: UNREACHABLE(); break; } } +void Context::getPointerv(GLenum pname, void **params) const +{ + mState.getPointerv(pname, params); +} + bool Context::getIndexedIntegerv(GLenum target, GLuint index, GLint *data) { // Queries about context capabilities and maximums are answered by Context. @@ -1093,20 +1255,6 @@ bool Context::getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *nu } } return true; - case GL_PIXEL_PACK_BUFFER_BINDING: - case GL_PIXEL_UNPACK_BUFFER_BINDING: - { - if (mExtensions.pixelBufferObject) - { - *type = GL_INT; - *numParams = 1; - } - else - { - return false; - } - } - return true; case GL_MAX_VIEWPORT_DIMS: { *type = GL_INT; @@ -1177,6 +1325,45 @@ bool Context::getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *nu *type = GL_FLOAT; *numParams = 1; return true; + case GL_TIMESTAMP_EXT: + if (!mExtensions.disjointTimerQuery) + { + return false; + } + *type = GL_INT_64_ANGLEX; + *numParams = 1; + return true; + case GL_GPU_DISJOINT_EXT: + if (!mExtensions.disjointTimerQuery) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + } + + if (mExtensions.debug) + { + switch (pname) + { + case GL_DEBUG_LOGGED_MESSAGES: + case GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH: + case GL_DEBUG_GROUP_STACK_DEPTH: + case GL_MAX_DEBUG_MESSAGE_LENGTH: + case GL_MAX_DEBUG_LOGGED_MESSAGES: + case GL_MAX_DEBUG_GROUP_STACK_DEPTH: + case GL_MAX_LABEL_LENGTH: + *type = GL_INT; + *numParams = 1; + return true; + + case GL_DEBUG_OUTPUT_SYNCHRONOUS: + case GL_DEBUG_OUTPUT: + *type = GL_BOOL; + *numParams = 1; + return true; + } } // Check for ES3.0+ parameter names which are also exposed as ES2 extensions @@ -1210,6 +1397,15 @@ bool Context::getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *nu *type = GL_INT; *numParams = 1; return true; + case GL_PIXEL_PACK_BUFFER_BINDING: + case GL_PIXEL_UNPACK_BUFFER_BINDING: + if ((mClientVersion < 3) && !mExtensions.pixelBufferObject) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; } if (mClientVersion < 3) @@ -1271,6 +1467,7 @@ bool Context::getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *nu case GL_TRANSFORM_FEEDBACK_ACTIVE: case GL_TRANSFORM_FEEDBACK_PAUSED: case GL_PRIMITIVE_RESTART_FIXED_INDEX: + case GL_RASTERIZER_DISCARD: { *type = GL_BOOL; *numParams = 1; @@ -1413,6 +1610,13 @@ void Context::recordError(const Error &error) if (error.isError()) { mErrors.insert(error.getCode()); + + if (!error.getMessage().empty()) + { + auto &debug = mState.getDebug(); + debug.insertMessage(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, error.getID(), + GL_DEBUG_SEVERITY_HIGH, error.getMessage()); + } } } @@ -1492,6 +1696,60 @@ EGLenum Context::getRenderBuffer() const } } +void Context::checkVertexArrayAllocation(GLuint vertexArray) +{ + // Only called after a prior call to Gen. + if (!getVertexArray(vertexArray)) + { + VertexArray *vertexArrayObject = + new VertexArray(mRenderer, vertexArray, MAX_VERTEX_ATTRIBS); + mVertexArrayMap[vertexArray] = vertexArrayObject; + } +} + +void Context::checkTransformFeedbackAllocation(GLuint transformFeedback) +{ + // Only called after a prior call to Gen. + if (!getTransformFeedback(transformFeedback)) + { + TransformFeedback *transformFeedbackObject = + new TransformFeedback(mRenderer->createTransformFeedback(), transformFeedback, mCaps); + transformFeedbackObject->addRef(); + mTransformFeedbackMap[transformFeedback] = transformFeedbackObject; + } +} + +Framebuffer *Context::checkFramebufferAllocation(GLuint framebuffer) +{ + // Can be called from Bind without a prior call to Gen. + auto framebufferIt = mFramebufferMap.find(framebuffer); + bool neverCreated = framebufferIt == mFramebufferMap.end(); + if (neverCreated || framebufferIt->second == nullptr) + { + Framebuffer *newFBO = new Framebuffer(mCaps, mRenderer, framebuffer); + if (neverCreated) + { + mFramebufferHandleAllocator.reserve(framebuffer); + mFramebufferMap[framebuffer] = newFBO; + return newFBO; + } + + framebufferIt->second = newFBO; + } + + return framebufferIt->second; +} + +bool Context::isVertexArrayGenerated(GLuint vertexArray) +{ + return mVertexArrayMap.find(vertexArray) != mVertexArrayMap.end(); +} + +bool Context::isTransformFeedbackGenerated(GLuint transformFeedback) +{ + return mTransformFeedbackMap.find(transformFeedback) != mTransformFeedbackMap.end(); +} + void Context::detachTexture(GLuint texture) { // Simple pass-through to State's detachTexture method, as textures do not require @@ -1503,20 +1761,15 @@ void Context::detachTexture(GLuint texture) void Context::detachBuffer(GLuint buffer) { - // Buffer detachment is handled by Context, because the buffer must also be - // attached from any VAOs in existence, and Context holds the VAO map. + // Simple pass-through to State's detachBuffer method, since + // only buffer attachments to container objects that are bound to the current context + // should be detached. And all those are available in State. - // [OpenGL ES 2.0.24] section 2.9 page 22: - // If a buffer object is deleted while it is bound, all bindings to that object in the current context - // (i.e. in the thread that called Delete-Buffers) are reset to zero. - - mState.removeArrayBufferBinding(buffer); - - // mark as freed among the vertex array objects - for (auto &vaoPair : mVertexArrayMap) - { - vaoPair.second->detachBuffer(buffer); - } + // [OpenGL ES 3.2] section 5.1.2 page 45: + // Attachments to unbound container objects, such as + // deletion of a buffer attached to a vertex array object which is not bound to the context, + // are not affected and continue to act as references on the deleted object + mState.detachBuffer(buffer); } void Context::detachFramebuffer(GLuint framebuffer) @@ -1734,6 +1987,13 @@ void Context::initCaps(GLuint clientVersion) //mExtensions.sRGB = false; } + // Explicitly enable GL_KHR_debug + mExtensions.debug = true; + mExtensions.maxDebugMessageLength = 1024; + mExtensions.maxDebugLoggedMessages = 1024; + mExtensions.maxDebugGroupStackDepth = 1024; + mExtensions.maxLabelLength = 1024; + // Apply implementation limits mCaps.maxVertexAttributes = std::min(mCaps.maxVertexAttributes, MAX_VERTEX_ATTRIBS); mCaps.maxVertexUniformBlocks = std::min(mCaps.maxVertexUniformBlocks, IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS); @@ -1779,20 +2039,386 @@ void Context::initCaps(GLuint clientVersion) void Context::syncRendererState() { const State::DirtyBits &dirtyBits = mState.getDirtyBits(); - if (dirtyBits.any()) - { - mRenderer->syncState(mState, dirtyBits); - mState.clearDirtyBits(); - } + mRenderer->syncState(mState, dirtyBits); + mState.clearDirtyBits(); + mState.syncDirtyObjects(); } void Context::syncRendererState(const State::DirtyBits &bitMask) { const State::DirtyBits &dirtyBits = (mState.getDirtyBits() & bitMask); - if (dirtyBits.any()) + mRenderer->syncState(mState, dirtyBits); + mState.clearDirtyBits(dirtyBits); + + // TODO(jmadill): Filter objects by bitMask somehow? + mState.syncDirtyObjects(); +} + +void Context::blitFramebuffer(GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter) +{ + Framebuffer *readFramebuffer = mState.getReadFramebuffer(); + ASSERT(readFramebuffer); + + Framebuffer *drawFramebuffer = mState.getDrawFramebuffer(); + ASSERT(drawFramebuffer); + + Rectangle srcArea(srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0); + Rectangle dstArea(dstX0, dstY0, dstX1 - dstX0, dstY1 - dstY0); + + syncRendererState(mState.blitStateBitMask()); + + Error error = drawFramebuffer->blit(mState, srcArea, dstArea, mask, filter, readFramebuffer); + if (error.isError()) { - mRenderer->syncState(mState, dirtyBits); - mState.clearDirtyBits(dirtyBits); + recordError(error); + return; } } + +void Context::clear(GLbitfield mask) +{ + // Sync the clear state + syncRendererState(mState.clearStateBitMask()); + + Error error = mState.getDrawFramebuffer()->clear(mData, mask); + if (error.isError()) + { + recordError(error); + } } + +void Context::clearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat *values) +{ + // Sync the clear state + syncRendererState(mState.clearStateBitMask()); + + Error error = mState.getDrawFramebuffer()->clearBufferfv(mData, buffer, drawbuffer, values); + if (error.isError()) + { + recordError(error); + } +} + +void Context::clearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint *values) +{ + // Sync the clear state + syncRendererState(mState.clearStateBitMask()); + + Error error = mState.getDrawFramebuffer()->clearBufferuiv(mData, buffer, drawbuffer, values); + if (error.isError()) + { + recordError(error); + } +} + +void Context::clearBufferiv(GLenum buffer, GLint drawbuffer, const GLint *values) +{ + // Sync the clear state + syncRendererState(mState.clearStateBitMask()); + + Error error = mState.getDrawFramebuffer()->clearBufferiv(mData, buffer, drawbuffer, values); + if (error.isError()) + { + recordError(error); + } +} + +void Context::clearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) +{ + Framebuffer *framebufferObject = mState.getDrawFramebuffer(); + ASSERT(framebufferObject); + + // If a buffer is not present, the clear has no effect + if (framebufferObject->getDepthbuffer() == nullptr && + framebufferObject->getStencilbuffer() == nullptr) + { + return; + } + + // Sync the clear state + syncRendererState(mState.clearStateBitMask()); + + Error error = framebufferObject->clearBufferfi(mData, buffer, drawbuffer, depth, stencil); + if (error.isError()) + { + recordError(error); + } +} + +void Context::readPixels(GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLvoid *pixels) +{ + // Sync pack state + syncRendererState(mState.packStateBitMask()); + + Framebuffer *framebufferObject = mState.getReadFramebuffer(); + ASSERT(framebufferObject); + + Rectangle area(x, y, width, height); + Error error = framebufferObject->readPixels(mState, area, format, type, pixels); + if (error.isError()) + { + recordError(error); + } +} + +void Context::copyTexImage2D(GLenum target, + GLint level, + GLenum internalformat, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border) +{ + // Only sync the read FBO + mState.syncDirtyObject(GL_READ_FRAMEBUFFER); + + Rectangle sourceArea(x, y, width, height); + + const Framebuffer *framebuffer = mState.getReadFramebuffer(); + Texture *texture = + getTargetTexture(IsCubeMapTextureTarget(target) ? GL_TEXTURE_CUBE_MAP : target); + Error error = texture->copyImage(target, level, sourceArea, internalformat, framebuffer); + if (error.isError()) + { + recordError(error); + } +} + +void Context::copyTexSubImage2D(GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height) +{ + // Only sync the read FBO + mState.syncDirtyObject(GL_READ_FRAMEBUFFER); + + Offset destOffset(xoffset, yoffset, 0); + Rectangle sourceArea(x, y, width, height); + + const Framebuffer *framebuffer = mState.getReadFramebuffer(); + Texture *texture = + getTargetTexture(IsCubeMapTextureTarget(target) ? GL_TEXTURE_CUBE_MAP : target); + Error error = texture->copySubImage(target, level, destOffset, sourceArea, framebuffer); + if (error.isError()) + { + recordError(error); + } +} + +void Context::copyTexSubImage3D(GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height) +{ + // Only sync the read FBO + mState.syncDirtyObject(GL_READ_FRAMEBUFFER); + + Offset destOffset(xoffset, yoffset, zoffset); + Rectangle sourceArea(x, y, width, height); + + const Framebuffer *framebuffer = mState.getReadFramebuffer(); + Texture *texture = getTargetTexture(target); + Error error = texture->copySubImage(target, level, destOffset, sourceArea, framebuffer); + if (error.isError()) + { + recordError(error); + } +} + +void Context::framebufferTexture2D(GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (texture != 0) + { + Texture *textureObj = getTexture(texture); + + ImageIndex index = ImageIndex::MakeInvalid(); + + if (textarget == GL_TEXTURE_2D) + { + index = ImageIndex::Make2D(level); + } + else + { + ASSERT(IsCubeMapTextureTarget(textarget)); + index = ImageIndex::MakeCube(textarget, level); + } + + framebuffer->setAttachment(GL_TEXTURE, attachment, index, textureObj); + } + else + { + framebuffer->resetAttachment(attachment); + } + + mState.setObjectDirty(target); +} + +void Context::framebufferRenderbuffer(GLenum target, + GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (renderbuffer != 0) + { + Renderbuffer *renderbufferObject = getRenderbuffer(renderbuffer); + framebuffer->setAttachment(GL_RENDERBUFFER, attachment, gl::ImageIndex::MakeInvalid(), + renderbufferObject); + } + else + { + framebuffer->resetAttachment(attachment); + } + + mState.setObjectDirty(target); +} + +void Context::framebufferTextureLayer(GLenum target, + GLenum attachment, + GLuint texture, + GLint level, + GLint layer) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (texture != 0) + { + Texture *textureObject = getTexture(texture); + + ImageIndex index = ImageIndex::MakeInvalid(); + + if (textureObject->getTarget() == GL_TEXTURE_3D) + { + index = ImageIndex::Make3D(level, layer); + } + else + { + ASSERT(textureObject->getTarget() == GL_TEXTURE_2D_ARRAY); + index = ImageIndex::Make2DArray(level, layer); + } + + framebuffer->setAttachment(GL_TEXTURE, attachment, index, textureObject); + } + else + { + framebuffer->resetAttachment(attachment); + } + + mState.setObjectDirty(target); +} + +void Context::drawBuffers(GLsizei n, const GLenum *bufs) +{ + Framebuffer *framebuffer = mState.getDrawFramebuffer(); + ASSERT(framebuffer); + framebuffer->setDrawBuffers(n, bufs); + mState.setObjectDirty(GL_DRAW_FRAMEBUFFER); +} + +void Context::readBuffer(GLenum mode) +{ + Framebuffer *readFBO = mState.getReadFramebuffer(); + readFBO->setReadBuffer(mode); + mState.setObjectDirty(GL_READ_FRAMEBUFFER); +} + +void Context::discardFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments) +{ + // Only sync the FBO + mState.syncDirtyObject(target); + + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + // The specification isn't clear what should be done when the framebuffer isn't complete. + // We leave it up to the framebuffer implementation to decide what to do. + Error error = framebuffer->discard(numAttachments, attachments); + if (error.isError()) + { + recordError(error); + } +} + +void Context::invalidateFramebuffer(GLenum target, + GLsizei numAttachments, + const GLenum *attachments) +{ + // Only sync the FBO + mState.syncDirtyObject(target); + + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (framebuffer->checkStatus(mData) == GL_FRAMEBUFFER_COMPLETE) + { + Error error = framebuffer->invalidate(numAttachments, attachments); + if (error.isError()) + { + recordError(error); + return; + } + } +} + +void Context::invalidateSubFramebuffer(GLenum target, + GLsizei numAttachments, + const GLenum *attachments, + GLint x, + GLint y, + GLsizei width, + GLsizei height) +{ + // Only sync the FBO + mState.syncDirtyObject(target); + + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (framebuffer->checkStatus(mData) == GL_FRAMEBUFFER_COMPLETE) + { + Rectangle area(x, y, width, height); + Error error = framebuffer->invalidateSub(numAttachments, attachments, area); + if (error.isError()) + { + recordError(error); + return; + } + } +} + +} // namespace gl diff --git a/gfx/angle/src/libANGLE/Context.h b/gfx/angle/src/libANGLE/Context.h index 44dbd962e6..6b82eb7cb9 100644 --- a/gfx/angle/src/libANGLE/Context.h +++ b/gfx/angle/src/libANGLE/Context.h @@ -33,6 +33,7 @@ class Renderer; namespace egl { +class AttributeMap; class Surface; struct Config; } @@ -58,7 +59,10 @@ class TransformFeedback; class Context final : public ValidationContext { public: - Context(const egl::Config *config, int clientVersion, const Context *shareContext, rx::Renderer *renderer, bool notifyResets, bool robustAccess); + Context(const egl::Config *config, + const Context *shareContext, + rx::Renderer *renderer, + const egl::AttributeMap &attribs); virtual ~Context(); @@ -107,8 +111,8 @@ class Context final : public ValidationContext void bindArrayBuffer(GLuint buffer); void bindElementArrayBuffer(GLuint buffer); void bindTexture(GLenum target, GLuint handle); - void bindReadFramebuffer(GLuint framebuffer); - void bindDrawFramebuffer(GLuint framebuffer); + void bindReadFramebuffer(GLuint framebufferHandle); + void bindDrawFramebuffer(GLuint framebufferHandle); void bindRenderbuffer(GLuint renderbuffer); void bindVertexArray(GLuint vertexArray); void bindSampler(GLuint textureUnit, GLuint sampler); @@ -125,6 +129,12 @@ class Context final : public ValidationContext Error beginQuery(GLenum target, GLuint query); Error endQuery(GLenum target); + Error queryCounter(GLuint id, GLenum target); + void getQueryiv(GLenum target, GLenum pname, GLint *params); + Error getQueryObjectiv(GLuint id, GLenum pname, GLint *params); + Error getQueryObjectuiv(GLuint id, GLenum pname, GLuint *params); + Error getQueryObjecti64v(GLuint id, GLenum pname, GLint64 *params); + Error getQueryObjectui64v(GLuint id, GLenum pname, GLuint64 *params); void setVertexAttribDivisor(GLuint index, GLuint divisor); @@ -133,18 +143,21 @@ class Context final : public ValidationContext GLint getSamplerParameteri(GLuint sampler, GLenum pname); GLfloat getSamplerParameterf(GLuint sampler, GLenum pname); - Buffer *getBuffer(GLuint handle); + Buffer *getBuffer(GLuint handle) const; FenceNV *getFenceNV(GLuint handle); FenceSync *getFenceSync(GLsync handle) const; Shader *getShader(GLuint handle) const; Program *getProgram(GLuint handle) const; Texture *getTexture(GLuint handle) const; Framebuffer *getFramebuffer(GLuint handle) const; - Renderbuffer *getRenderbuffer(GLuint handle); + Renderbuffer *getRenderbuffer(GLuint handle) const; VertexArray *getVertexArray(GLuint handle) const; Sampler *getSampler(GLuint handle) const; Query *getQuery(GLuint handle, bool create, GLenum type); + Query *getQuery(GLuint handle) const; TransformFeedback *getTransformFeedback(GLuint handle) const; + LabeledObject *getLabeledObject(GLenum identifier, GLuint name) const; + LabeledObject *getLabeledObjectFromPtr(const void *ptr) const; Texture *getTargetTexture(GLenum target) const; Texture *getSamplerTexture(unsigned int sampler, GLenum type) const; @@ -153,10 +166,14 @@ class Context final : public ValidationContext bool isSampler(GLuint samplerName) const; + bool isVertexArrayGenerated(GLuint vertexArray); + bool isTransformFeedbackGenerated(GLuint vertexArray); + void getBooleanv(GLenum pname, GLboolean *params); void getFloatv(GLenum pname, GLfloat *params); void getIntegerv(GLenum pname, GLint *params); void getInteger64v(GLenum pname, GLint64 *params); + void getPointerv(GLenum pname, void **params) const; bool getIndexedIntegerv(GLenum target, GLuint index, GLint *data); bool getIndexedInteger64v(GLenum target, GLuint index, GLint64 *data); @@ -164,6 +181,12 @@ class Context final : public ValidationContext bool getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *numParams); bool getIndexedQueryParameterInfo(GLenum target, GLenum *type, unsigned int *numParams); + void clear(GLbitfield mask); + void clearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat *values); + void clearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint *values); + void clearBufferiv(GLenum buffer, GLint drawbuffer, const GLint *values); + void clearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); + Error drawArrays(GLenum mode, GLint first, GLsizei count); Error drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount); @@ -186,6 +209,83 @@ class Context final : public ValidationContext const GLvoid *indices, const IndexRange &indexRange); + void blitFramebuffer(GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter); + + void readPixels(GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLvoid *pixels); + + void copyTexImage2D(GLenum target, + GLint level, + GLenum internalformat, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border); + + void copyTexSubImage2D(GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height); + + void copyTexSubImage3D(GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height); + + void framebufferTexture2D(GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level); + + void framebufferRenderbuffer(GLenum target, + GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer); + + void framebufferTextureLayer(GLenum target, + GLenum attachment, + GLuint texture, + GLint level, + GLint layer); + + void drawBuffers(GLsizei n, const GLenum *bufs); + void readBuffer(GLenum mode); + + void discardFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments); + void invalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments); + void invalidateSubFramebuffer(GLenum target, + GLsizei numAttachments, + const GLenum *attachments, + GLint x, + GLint y, + GLsizei width, + GLsizei height); + Error flush(); Error finish(); @@ -217,6 +317,10 @@ class Context final : public ValidationContext void syncRendererState(const State::DirtyBits &bitMask); private: + void checkVertexArrayAllocation(GLuint vertexArray); + void checkTransformFeedbackAllocation(GLuint transformFeedback); + Framebuffer *checkFramebufferAllocation(GLuint framebufferHandle); + void detachBuffer(GLuint buffer); void detachTexture(GLuint texture); void detachFramebuffer(GLuint framebuffer); @@ -265,7 +369,6 @@ class Context final : public ValidationContext VertexArrayMap mVertexArrayMap; HandleAllocator mVertexArrayHandleAllocator; - BindingPointer mTransformFeedbackZero; typedef std::map TransformFeedbackMap; TransformFeedbackMap mTransformFeedbackMap; HandleAllocator mTransformFeedbackAllocator; @@ -289,6 +392,6 @@ class Context final : public ValidationContext ResourceManager *mResourceManager; }; -} +} // namespace gl #endif // LIBANGLE_CONTEXT_H_ diff --git a/gfx/angle/src/libANGLE/Data.cpp b/gfx/angle/src/libANGLE/Data.cpp index 57657c40b4..83f04b5a0b 100644 --- a/gfx/angle/src/libANGLE/Data.cpp +++ b/gfx/angle/src/libANGLE/Data.cpp @@ -40,7 +40,8 @@ ValidationContext::ValidationContext(GLint clientVersion, const TextureCapsMap &textureCaps, const Extensions &extensions, const ResourceManager *resourceManager, - const Limitations &limitations) + const Limitations &limitations, + bool skipValidation) : mData(reinterpret_cast(this), clientVersion, state, @@ -48,7 +49,8 @@ ValidationContext::ValidationContext(GLint clientVersion, textureCaps, extensions, resourceManager, - limitations) + limitations), + mSkipValidation(skipValidation) { } -} +} // namespace gl diff --git a/gfx/angle/src/libANGLE/Data.h b/gfx/angle/src/libANGLE/Data.h index eac5ffed6a..f7230d74bc 100644 --- a/gfx/angle/src/libANGLE/Data.h +++ b/gfx/angle/src/libANGLE/Data.h @@ -47,7 +47,8 @@ class ValidationContext : angle::NonCopyable const TextureCapsMap &textureCaps, const Extensions &extensions, const ResourceManager *resourceManager, - const Limitations &limitations); + const Limitations &limitations, + bool skipValidation); virtual ~ValidationContext() {} virtual void recordError(const Error &error) = 0; @@ -59,9 +60,11 @@ class ValidationContext : angle::NonCopyable const TextureCapsMap &getTextureCaps() const { return *mData.textureCaps; } const Extensions &getExtensions() const { return *mData.extensions; } const Limitations &getLimitations() const { return *mData.limitations; } + bool skipValidation() const { return mSkipValidation; } protected: Data mData; + bool mSkipValidation; }; } diff --git a/gfx/angle/src/libANGLE/Debug.cpp b/gfx/angle/src/libANGLE/Debug.cpp new file mode 100644 index 0000000000..30321f4160 --- /dev/null +++ b/gfx/angle/src/libANGLE/Debug.cpp @@ -0,0 +1,303 @@ +// +// Copyright (c) 2015 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. +// + +// Debug.cpp: Defines debug state used for GL_KHR_debug + +#include "libANGLE/Debug.h" + +#include "common/debug.h" + +#include +#include + +namespace gl +{ + +Debug::Debug() + : mOutputEnabled(false), + mCallbackFunction(nullptr), + mCallbackUserParam(nullptr), + mMessages(), + mMaxLoggedMessages(0), + mOutputSynchronous(false), + mGroups() +{ + pushDefaultGroup(); +} + +void Debug::setMaxLoggedMessages(GLuint maxLoggedMessages) +{ + mMaxLoggedMessages = maxLoggedMessages; +} + +void Debug::setOutputEnabled(bool enabled) +{ + mOutputEnabled = enabled; +} + +bool Debug::isOutputEnabled() const +{ + return mOutputEnabled; +} + +void Debug::setOutputSynchronous(bool synchronous) +{ + mOutputSynchronous = synchronous; +} + +bool Debug::isOutputSynchronous() const +{ + return mOutputSynchronous; +} + +void Debug::setCallback(GLDEBUGPROCKHR callback, const void *userParam) +{ + mCallbackFunction = callback; + mCallbackUserParam = userParam; +} + +GLDEBUGPROCKHR Debug::getCallback() const +{ + return mCallbackFunction; +} + +const void *Debug::getUserParam() const +{ + return mCallbackUserParam; +} + +void Debug::insertMessage(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + const std::string &message) +{ + std::string messageCopy(message); + insertMessage(source, type, id, severity, std::move(messageCopy)); +} + +void Debug::insertMessage(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + std::string &&message) +{ + if (!isMessageEnabled(source, type, id, severity)) + { + return; + } + + if (mCallbackFunction != nullptr) + { + // TODO(geofflang) Check the synchronous flag and potentially flush messages from another + // thread. + mCallbackFunction(source, type, id, severity, static_cast(message.length()), + message.c_str(), mCallbackUserParam); + } + else + { + if (mMessages.size() >= mMaxLoggedMessages) + { + // Drop messages over the limit + return; + } + + Message m; + m.source = source; + m.type = type; + m.id = id; + m.severity = severity; + m.message = std::move(message); + + mMessages.push_back(std::move(m)); + } +} + +size_t Debug::getMessages(GLuint count, + GLsizei bufSize, + GLenum *sources, + GLenum *types, + GLuint *ids, + GLenum *severities, + GLsizei *lengths, + GLchar *messageLog) +{ + size_t messageCount = 0; + size_t messageStringIndex = 0; + while (messageCount <= count && !mMessages.empty()) + { + const Message &m = mMessages.front(); + + if (messageLog != nullptr) + { + // Check that this message can fit in the message buffer + if (messageStringIndex + m.message.length() + 1 > static_cast(bufSize)) + { + break; + } + + std::copy(m.message.begin(), m.message.end(), messageLog + messageStringIndex); + messageStringIndex += m.message.length(); + + messageLog[messageStringIndex] = '\0'; + messageStringIndex += 1; + } + + if (sources != nullptr) + { + sources[messageCount] = m.source; + } + + if (types != nullptr) + { + types[messageCount] = m.type; + } + + if (ids != nullptr) + { + ids[messageCount] = m.id; + } + + if (severities != nullptr) + { + severities[messageCount] = m.severity; + } + + if (lengths != nullptr) + { + lengths[messageCount] = static_cast(m.message.length()); + } + + mMessages.pop_front(); + + messageCount++; + } + + return messageCount; +} + +size_t Debug::getNextMessageLength() const +{ + return mMessages.empty() ? 0 : mMessages.front().message.length(); +} + +size_t Debug::getMessageCount() const +{ + return mMessages.size(); +} + +void Debug::setMessageControl(GLenum source, + GLenum type, + GLenum severity, + std::vector &&ids, + bool enabled) +{ + Control c; + c.source = source; + c.type = type; + c.severity = severity; + c.ids = std::move(ids); + c.enabled = enabled; + + auto &controls = mGroups.back().controls; + controls.push_back(std::move(c)); +} + +void Debug::pushGroup(GLenum source, GLuint id, std::string &&message) +{ + insertMessage(source, GL_DEBUG_TYPE_PUSH_GROUP, id, GL_DEBUG_SEVERITY_NOTIFICATION, + std::string(message)); + + Group g; + g.source = source; + g.id = id; + g.message = std::move(message); + mGroups.push_back(std::move(g)); +} + +void Debug::popGroup() +{ + // Make sure the default group is not about to be popped + ASSERT(mGroups.size() > 1); + + Group g = mGroups.back(); + mGroups.pop_back(); + + insertMessage(g.source, GL_DEBUG_TYPE_POP_GROUP, g.id, GL_DEBUG_SEVERITY_NOTIFICATION, + g.message); +} + +size_t Debug::getGroupStackDepth() const +{ + return mGroups.size(); +} + +bool Debug::isMessageEnabled(GLenum source, GLenum type, GLuint id, GLenum severity) const +{ + if (!mOutputEnabled) + { + return false; + } + + for (auto groupIter = mGroups.rbegin(); groupIter != mGroups.rend(); groupIter++) + { + const auto &controls = groupIter->controls; + for (auto controlIter = controls.rbegin(); controlIter != controls.rend(); controlIter++) + { + const auto &control = *controlIter; + + if (control.source != GL_DONT_CARE && control.source != source) + { + continue; + } + + if (control.type != GL_DONT_CARE && control.type != type) + { + continue; + } + + if (control.severity != GL_DONT_CARE && control.severity != severity) + { + continue; + } + + if (!control.ids.empty() && + std::find(control.ids.begin(), control.ids.end(), id) == control.ids.end()) + { + continue; + } + + return control.enabled; + } + } + + return true; +} + +void Debug::pushDefaultGroup() +{ + Group g; + g.source = GL_NONE; + g.id = 0; + g.message = ""; + + Control c0; + c0.source = GL_DONT_CARE; + c0.type = GL_DONT_CARE; + c0.severity = GL_DONT_CARE; + c0.enabled = true; + g.controls.push_back(std::move(c0)); + + Control c1; + c1.source = GL_DONT_CARE; + c1.type = GL_DONT_CARE; + c1.severity = GL_DEBUG_SEVERITY_LOW; + c1.enabled = false; + g.controls.push_back(std::move(c1)); + + mGroups.push_back(std::move(g)); +} +} // namespace gl diff --git a/gfx/angle/src/libANGLE/Debug.h b/gfx/angle/src/libANGLE/Debug.h new file mode 100644 index 0000000000..f545b815e4 --- /dev/null +++ b/gfx/angle/src/libANGLE/Debug.h @@ -0,0 +1,120 @@ +// +// Copyright (c) 2015 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. +// + +// Debug.h: Defines debug state used for GL_KHR_debug + +#ifndef LIBANGLE_DEBUG_H_ +#define LIBANGLE_DEBUG_H_ + +#include "angle_gl.h" +#include "common/angleutils.h" + +#include +#include +#include + +namespace gl +{ + +class LabeledObject +{ + public: + virtual ~LabeledObject() {} + virtual void setLabel(const std::string &label) = 0; + virtual const std::string &getLabel() const = 0; +}; + +class Debug : angle::NonCopyable +{ + public: + Debug(); + + void setMaxLoggedMessages(GLuint maxLoggedMessages); + + void setOutputEnabled(bool enabled); + bool isOutputEnabled() const; + + void setOutputSynchronous(bool synchronous); + bool isOutputSynchronous() const; + + void setCallback(GLDEBUGPROCKHR callback, const void *userParam); + GLDEBUGPROCKHR getCallback() const; + const void *getUserParam() const; + + void insertMessage(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + const std::string &message); + void insertMessage(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + std::string &&message); + + void setMessageControl(GLenum source, + GLenum type, + GLenum severity, + std::vector &&ids, + bool enabled); + size_t getMessages(GLuint count, + GLsizei bufSize, + GLenum *sources, + GLenum *types, + GLuint *ids, + GLenum *severities, + GLsizei *lengths, + GLchar *messageLog); + size_t getNextMessageLength() const; + size_t getMessageCount() const; + + void pushGroup(GLenum source, GLuint id, std::string &&message); + void popGroup(); + size_t getGroupStackDepth() const; + + private: + bool isMessageEnabled(GLenum source, GLenum type, GLuint id, GLenum severity) const; + + void pushDefaultGroup(); + + struct Message + { + GLenum source; + GLenum type; + GLuint id; + GLenum severity; + std::string message; + }; + + struct Control + { + GLenum source; + GLenum type; + GLenum severity; + std::vector ids; + bool enabled; + }; + + struct Group + { + GLenum source; + GLuint id; + std::string message; + + std::vector controls; + }; + + bool mOutputEnabled; + GLDEBUGPROCKHR mCallbackFunction; + const void *mCallbackUserParam; + std::deque mMessages; + GLuint mMaxLoggedMessages; + bool mOutputSynchronous; + std::vector mGroups; +}; +} // namespace gl + +#endif // LIBANGLE_DEBUG_H_ diff --git a/gfx/angle/src/libANGLE/Device.cpp b/gfx/angle/src/libANGLE/Device.cpp index d00bf4ddaa..eb30b2023f 100644 --- a/gfx/angle/src/libANGLE/Device.cpp +++ b/gfx/angle/src/libANGLE/Device.cpp @@ -18,6 +18,10 @@ #include "common/platform.h" #include "libANGLE/renderer/DeviceImpl.h" +#if defined(ANGLE_ENABLE_D3D11) +#include "libANGLE/renderer/d3d/DeviceD3D.h" +#endif + namespace egl { @@ -31,16 +35,67 @@ static std::string GenerateExtensionsString(const T &extensions) return stream.str(); } -Device::Device(Display *display, rx::DeviceImpl *impl) - : mDisplay(display), - mImplementation(impl) +typedef std::set DeviceSet; +static DeviceSet *GetDeviceSet() +{ + static DeviceSet devices; + return &devices; +} + +// Static factory methods +egl::Error Device::CreateDevice(void *devicePointer, EGLint deviceType, Device **outDevice) +{ +#if defined(ANGLE_ENABLE_D3D11) + if (deviceType == EGL_D3D11_DEVICE_ANGLE) + { + rx::DeviceD3D *deviceD3D = new rx::DeviceD3D(); + egl::Error error = deviceD3D->initialize(devicePointer, deviceType, EGL_TRUE); + if (error.isError()) + { + *outDevice = nullptr; + return error; + } + + *outDevice = new Device(nullptr, deviceD3D); + GetDeviceSet()->insert(*outDevice); + return egl::Error(EGL_SUCCESS); + } +#endif + + // Note that creating an EGL device from inputted D3D9 parameters isn't currently supported + *outDevice = nullptr; + return egl::Error(EGL_BAD_ATTRIBUTE); +} + +egl::Error Device::CreateDevice(Display *owningDisplay, rx::DeviceImpl *impl, Device **outDevice) +{ + *outDevice = new Device(owningDisplay, impl); + GetDeviceSet()->insert(*outDevice); + return egl::Error(EGL_SUCCESS); +} + +bool Device::IsValidDevice(Device *device) +{ + const DeviceSet *deviceSet = GetDeviceSet(); + return deviceSet->find(device) != deviceSet->end(); +} + +Device::Device(Display *owningDisplay, rx::DeviceImpl *impl) + : mOwningDisplay(owningDisplay), mImplementation(impl) { initDeviceExtensions(); } Device::~Device() { + ASSERT(GetDeviceSet()->find(this) != GetDeviceSet()->end()); + GetDeviceSet()->erase(this); + if (mImplementation->deviceExternallySourced()) + { + // If the device isn't externally sourced then it is up to the renderer to delete the impl + SafeDelete(mImplementation); + } } Error Device::getDevice(EGLAttrib *value) diff --git a/gfx/angle/src/libANGLE/Device.h b/gfx/angle/src/libANGLE/Device.h index 5c5f2d5b52..4bc58ff043 100644 --- a/gfx/angle/src/libANGLE/Device.h +++ b/gfx/angle/src/libANGLE/Device.h @@ -24,11 +24,10 @@ namespace egl class Device final : angle::NonCopyable { public: - Device(Display *display, rx::DeviceImpl *impl); virtual ~Device(); Error getDevice(EGLAttrib *value); - Display *getDisplay() { return mDisplay; }; + Display *getOwningDisplay() { return mOwningDisplay; }; EGLint getType(); const DeviceExtensions &getExtensions() const; @@ -36,10 +35,18 @@ class Device final : angle::NonCopyable rx::DeviceImpl *getImplementation() { return mImplementation; } + static egl::Error CreateDevice(void *devicePointer, EGLint deviceType, Device **outDevice); + static egl::Error CreateDevice(Display *owningDisplay, + rx::DeviceImpl *impl, + Device **outDevice); + + static bool IsValidDevice(Device *device); + private: + Device(Display *owningDisplay, rx::DeviceImpl *impl); void initDeviceExtensions(); - Display *mDisplay; + Display *mOwningDisplay; rx::DeviceImpl *mImplementation; DeviceExtensions mDeviceExtensions; diff --git a/gfx/angle/src/libANGLE/Display.cpp b/gfx/angle/src/libANGLE/Display.cpp index 083533617b..486c74abc0 100644 --- a/gfx/angle/src/libANGLE/Display.cpp +++ b/gfx/angle/src/libANGLE/Display.cpp @@ -85,14 +85,52 @@ static WindowSurfaceMap *GetWindowSurfaces() return &windowSurfaces; } -typedef std::map DisplayMap; -static DisplayMap *GetDisplayMap() +typedef std::map ANGLEPlatformDisplayMap; +static ANGLEPlatformDisplayMap *GetANGLEPlatformDisplayMap() { - static DisplayMap displays; + static ANGLEPlatformDisplayMap displays; return &displays; } -rx::DisplayImpl *CreateDisplayImpl(const AttributeMap &attribMap) +typedef std::map DevicePlatformDisplayMap; +static DevicePlatformDisplayMap *GetDevicePlatformDisplayMap() +{ + static DevicePlatformDisplayMap displays; + return &displays; +} + +rx::DisplayImpl *CreateDisplayFromDevice(Device *eglDevice) +{ + rx::DisplayImpl *impl = nullptr; + + switch (eglDevice->getType()) + { +#if defined(ANGLE_ENABLE_D3D11) + case EGL_D3D11_DEVICE_ANGLE: + impl = new rx::DisplayD3D(); + break; +#endif +#if defined(ANGLE_ENABLE_D3D9) + case EGL_D3D9_DEVICE_ANGLE: + // Currently the only way to get EGLDeviceEXT representing a D3D9 device + // is to retrieve one from an already-existing EGLDisplay. + // When eglGetPlatformDisplayEXT is called with a D3D9 EGLDeviceEXT, + // the already-existing display should be returned. + // Therefore this codepath to create a new display from the device + // should never be hit. + UNREACHABLE(); + break; +#endif + default: + UNREACHABLE(); + break; + } + + ASSERT(impl != nullptr); + return impl; +} + +rx::DisplayImpl *CreateDisplayFromAttribs(const AttributeMap &attribMap) { rx::DisplayImpl *impl = nullptr; EGLint displayType = attribMap.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE); @@ -138,26 +176,40 @@ rx::DisplayImpl *CreateDisplayImpl(const AttributeMap &attribMap) #endif break; +#if defined(ANGLE_ENABLE_OPENGL) + case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE: +#if defined(ANGLE_PLATFORM_WINDOWS) + impl = new rx::DisplayWGL(); +#elif defined(ANGLE_USE_X11) + impl = new rx::DisplayGLX(); +#else + // No GLES support on this platform, fail display creation. + impl = nullptr; +#endif + break; +#endif + default: UNREACHABLE(); break; } - ASSERT(impl != nullptr); return impl; } } -Display *Display::getDisplay(EGLNativeDisplayType displayId, const AttributeMap &attribMap) +Display *Display::GetDisplayFromAttribs(void *native_display, const AttributeMap &attribMap) { // Initialize the global platform if not already InitDefaultPlatformImpl(); - Display *display = NULL; + Display *display = nullptr; - DisplayMap *displays = GetDisplayMap(); - DisplayMap::const_iterator iter = displays->find(displayId); + EGLNativeDisplayType displayId = reinterpret_cast(native_display); + + ANGLEPlatformDisplayMap *displays = GetANGLEPlatformDisplayMap(); + ANGLEPlatformDisplayMap::const_iterator iter = displays->find(displayId); if (iter != displays->end()) { display = iter->second; @@ -171,21 +223,77 @@ Display *Display::getDisplay(EGLNativeDisplayType displayId, const AttributeMap return NULL; } - display = new Display(displayId); + display = new Display(EGL_PLATFORM_ANGLE_ANGLE, displayId, nullptr); displays->insert(std::make_pair(displayId, display)); } // Apply new attributes if the display is not initialized yet. if (!display->isInitialized()) { - rx::DisplayImpl* impl = CreateDisplayImpl(attribMap); + rx::DisplayImpl *impl = CreateDisplayFromAttribs(attribMap); + if (impl == nullptr) + { + // No valid display implementation for these attributes + return nullptr; + } + display->setAttributes(impl, attribMap); } return display; } -Display::Display(EGLNativeDisplayType displayId) +Display *Display::GetDisplayFromDevice(void *native_display) +{ + // Initialize the global platform if not already + InitDefaultPlatformImpl(); + + Display *display = nullptr; + + Device *eglDevice = reinterpret_cast(native_display); + ASSERT(Device::IsValidDevice(eglDevice)); + + ANGLEPlatformDisplayMap *anglePlatformDisplays = GetANGLEPlatformDisplayMap(); + DevicePlatformDisplayMap *devicePlatformDisplays = GetDevicePlatformDisplayMap(); + + // First see if this eglDevice is in use by a Display created using ANGLE platform + for (auto &displayMapEntry : *anglePlatformDisplays) + { + egl::Display *iterDisplay = displayMapEntry.second; + if (iterDisplay->getDevice() == eglDevice) + { + display = iterDisplay; + } + } + + if (display == nullptr) + { + // See if the eglDevice is in use by a Display created using the DEVICE platform + DevicePlatformDisplayMap::const_iterator iter = devicePlatformDisplays->find(eglDevice); + if (iter != devicePlatformDisplays->end()) + { + display = iter->second; + } + } + + if (display == nullptr) + { + // Otherwise create a new Display + display = new Display(EGL_PLATFORM_DEVICE_EXT, 0, eglDevice); + devicePlatformDisplays->insert(std::make_pair(eglDevice, display)); + } + + // Apply new attributes if the display is not initialized yet. + if (!display->isInitialized()) + { + rx::DisplayImpl *impl = CreateDisplayFromDevice(eglDevice); + display->setAttributes(impl, egl::AttributeMap()); + } + + return display; +} + +Display::Display(EGLenum platform, EGLNativeDisplayType displayId, Device *eglDevice) : mImplementation(nullptr), mDisplayId(displayId), mAttributeMap(), @@ -196,7 +304,8 @@ Display::Display(EGLNativeDisplayType displayId) mDisplayExtensions(), mDisplayExtensionString(), mVendorString(), - mDevice(nullptr) + mDevice(eglDevice), + mPlatform(platform) { } @@ -204,11 +313,27 @@ Display::~Display() { terminate(); - DisplayMap *displays = GetDisplayMap(); - DisplayMap::iterator iter = displays->find(mDisplayId); - if (iter != displays->end()) + if (mPlatform == EGL_PLATFORM_ANGLE_ANGLE) { - displays->erase(iter); + ANGLEPlatformDisplayMap *displays = GetANGLEPlatformDisplayMap(); + ANGLEPlatformDisplayMap::iterator iter = displays->find(mDisplayId); + if (iter != displays->end()) + { + displays->erase(iter); + } + } + else if (mPlatform == EGL_PLATFORM_DEVICE_EXT) + { + DevicePlatformDisplayMap *displays = GetDevicePlatformDisplayMap(); + DevicePlatformDisplayMap::iterator iter = displays->find(mDevice); + if (iter != displays->end()) + { + displays->erase(iter); + } + } + else + { + UNREACHABLE(); } SafeDelete(mDevice); @@ -264,19 +389,34 @@ Error Display::initialize() initDisplayExtensions(); initVendorString(); - if (mDisplayExtensions.deviceQuery) + // Populate the Display's EGLDeviceEXT if the Display wasn't created using one + if (mPlatform != EGL_PLATFORM_DEVICE_EXT) { - rx::DeviceImpl *impl = nullptr; - error = mImplementation->getDevice(&impl); - if (error.isError()) + if (mDisplayExtensions.deviceQuery) { - return error; + rx::DeviceImpl *impl = nullptr; + error = mImplementation->getDevice(&impl); + if (error.isError()) + { + return error; + } + + error = Device::CreateDevice(this, impl, &mDevice); + if (error.isError()) + { + return error; + } + } + else + { + mDevice = nullptr; } - mDevice = new Device(this, impl); } else { - mDevice = nullptr; + // For EGL_PLATFORM_DEVICE_EXT, mDevice should always be populated using + // an external device + ASSERT(mDevice != nullptr); } mInitialized = true; @@ -298,8 +438,20 @@ void Display::terminate() destroyImage(*mImageSet.begin()); } + while (!mImplementation->getSurfaceSet().empty()) + { + destroySurface(*mImplementation->getSurfaceSet().begin()); + } + mConfigSet.clear(); + if (mDevice != nullptr && mDevice->getOwningDisplay() != nullptr) + { + // Don't delete the device if it was created externally using eglCreateDeviceANGLE + // We also shouldn't set it to null in case eglInitialize() is called again later + SafeDelete(mDevice); + } + mImplementation->terminate(); mInitialized = false; @@ -349,6 +501,15 @@ bool Display::getConfigAttrib(const Config *configuration, EGLint attribute, EGL case EGL_MAX_PBUFFER_WIDTH: *value = configuration->maxPBufferWidth; break; case EGL_MAX_PBUFFER_HEIGHT: *value = configuration->maxPBufferHeight; break; case EGL_MAX_PBUFFER_PIXELS: *value = configuration->maxPBufferPixels; break; + + case EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE: + if (!getExtensions().surfaceOrientation) + { + return false; + } + *value = configuration->optimalOrientation; + break; + default: return false; } @@ -552,12 +713,8 @@ Error Display::createContext(const Config *configuration, gl::Context *shareCont } } - gl::Context *context = nullptr; - Error error = mImplementation->createContext(configuration, shareContext, attribs, &context); - if (error.isError()) - { - return error; - } + gl::Context *context = *outContext = + mImplementation->createContext(configuration, shareContext, attribs); ASSERT(context != nullptr); mContextSet.insert(context); @@ -657,6 +814,16 @@ void Display::notifyDeviceLost() } } +Error Display::waitClient() const +{ + return mImplementation->waitClient(); +} + +Error Display::waitNative(EGLint engine, egl::Surface *drawSurface, egl::Surface *readSurface) const +{ + return mImplementation->waitNative(engine, drawSurface, readSurface); +} + const Caps &Display::getCaps() const { return mCaps; @@ -705,12 +872,23 @@ static ClientExtensions GenerateClientExtensions() #if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11) extensions.platformANGLED3D = true; + extensions.platformDevice = true; #endif #if defined(ANGLE_ENABLE_OPENGL) extensions.platformANGLEOpenGL = true; #endif +#if defined(ANGLE_ENABLE_D3D11) + extensions.deviceCreation = true; + extensions.deviceCreationD3D11 = true; + extensions.experimentalPresentPath = true; +#endif + +#if defined(ANGLE_USE_X11) + extensions.x11Visual = true; +#endif + extensions.clientGetAllProcAddresses = true; return extensions; @@ -755,8 +933,17 @@ bool Display::isValidNativeWindow(EGLNativeWindowType window) const bool Display::isValidDisplay(const egl::Display *display) { - const DisplayMap *displayMap = GetDisplayMap(); - for (const auto &displayPair : *displayMap) + const ANGLEPlatformDisplayMap *anglePlatformDisplayMap = GetANGLEPlatformDisplayMap(); + for (const auto &displayPair : *anglePlatformDisplayMap) + { + if (displayPair.second == display) + { + return true; + } + } + + const DevicePlatformDisplayMap *devicePlatformDisplayMap = GetDevicePlatformDisplayMap(); + for (const auto &displayPair : *devicePlatformDisplayMap) { if (displayPair.second == display) { diff --git a/gfx/angle/src/libANGLE/Display.h b/gfx/angle/src/libANGLE/Display.h index b451322dd8..f80e308347 100644 --- a/gfx/angle/src/libANGLE/Display.h +++ b/gfx/angle/src/libANGLE/Display.h @@ -44,7 +44,8 @@ class Display final : angle::NonCopyable Error initialize(); void terminate(); - static egl::Display *getDisplay(EGLNativeDisplayType displayId, const AttributeMap &attribMap); + static egl::Display *GetDisplayFromDevice(void *native_display); + static egl::Display *GetDisplayFromAttribs(void *native_display, const AttributeMap &attribMap); static const ClientExtensions &getClientExtensions(); static const std::string &getClientExtensionString(); @@ -90,6 +91,9 @@ class Display final : angle::NonCopyable bool testDeviceLost(); void notifyDeviceLost(); + Error waitClient() const; + Error waitNative(EGLint engine, egl::Surface *drawSurface, egl::Surface *readSurface) const; + const Caps &getCaps() const; const DisplayExtensions &getExtensions() const; @@ -101,9 +105,10 @@ class Display final : angle::NonCopyable rx::DisplayImpl *getImplementation() { return mImplementation; } Device *getDevice() const; + EGLenum getPlatform() const { return mPlatform; } private: - Display(EGLNativeDisplayType displayId); + Display(EGLenum platform, EGLNativeDisplayType displayId, Device *eglDevice); void setAttributes(rx::DisplayImpl *impl, const AttributeMap &attribMap); @@ -135,6 +140,7 @@ class Display final : angle::NonCopyable std::string mVendorString; Device *mDevice; + EGLenum mPlatform; }; } diff --git a/gfx/angle/src/libANGLE/Error.cpp b/gfx/angle/src/libANGLE/Error.cpp index 00241451f5..fed1594972 100644 --- a/gfx/angle/src/libANGLE/Error.cpp +++ b/gfx/angle/src/libANGLE/Error.cpp @@ -16,9 +16,16 @@ namespace gl { -Error::Error(GLenum errorCode, const char *msg, ...) - : mCode(errorCode), - mMessage(nullptr) +Error::Error(GLenum errorCode, const char *msg, ...) : mCode(errorCode), mID(errorCode) +{ + va_list vararg; + va_start(vararg, msg); + createMessageString(); + *mMessage = FormatString(msg, vararg); + va_end(vararg); +} + +Error::Error(GLenum errorCode, GLuint id, const char *msg, ...) : mCode(errorCode), mID(id) { va_list vararg; va_start(vararg, msg); @@ -29,9 +36,9 @@ Error::Error(GLenum errorCode, const char *msg, ...) void Error::createMessageString() const { - if (mMessage == nullptr) + if (!mMessage) { - mMessage = new std::string(); + mMessage.reset(new std::string); } } @@ -47,8 +54,7 @@ bool Error::operator==(const Error &other) const return false; // TODO(jmadill): Compare extended error codes instead of strings. - if ((mMessage == nullptr || other.mMessage == nullptr) && - ((mMessage == nullptr) != (other.mMessage == nullptr))) + if ((!mMessage || !other.mMessage) && (!mMessage != !other.mMessage)) return false; return (*mMessage == *other.mMessage); @@ -63,10 +69,7 @@ bool Error::operator!=(const Error &other) const namespace egl { -Error::Error(EGLint errorCode, const char *msg, ...) - : mCode(errorCode), - mID(0), - mMessage(nullptr) +Error::Error(EGLint errorCode, const char *msg, ...) : mCode(errorCode), mID(0) { va_list vararg; va_start(vararg, msg); @@ -75,10 +78,7 @@ Error::Error(EGLint errorCode, const char *msg, ...) va_end(vararg); } -Error::Error(EGLint errorCode, EGLint id, const char *msg, ...) - : mCode(errorCode), - mID(id), - mMessage(nullptr) +Error::Error(EGLint errorCode, EGLint id, const char *msg, ...) : mCode(errorCode), mID(id) { va_list vararg; va_start(vararg, msg); @@ -86,11 +86,12 @@ Error::Error(EGLint errorCode, EGLint id, const char *msg, ...) *mMessage = FormatString(msg, vararg); va_end(vararg); } + void Error::createMessageString() const { - if (mMessage == nullptr) + if (!mMessage) { - mMessage = new std::string(); + mMessage.reset(new std::string); } } diff --git a/gfx/angle/src/libANGLE/Error.h b/gfx/angle/src/libANGLE/Error.h index 81c9330fc0..a5f760956a 100644 --- a/gfx/angle/src/libANGLE/Error.h +++ b/gfx/angle/src/libANGLE/Error.h @@ -13,6 +13,7 @@ #include #include +#include namespace gl { @@ -22,15 +23,15 @@ class Error final public: explicit inline Error(GLenum errorCode); Error(GLenum errorCode, const char *msg, ...); + Error(GLenum errorCode, GLuint id, const char *msg, ...); inline Error(const Error &other); inline Error(Error &&other); - inline ~Error(); - inline Error &operator=(const Error &other); inline Error &operator=(Error &&other); inline GLenum getCode() const; + inline GLuint getID() const; inline bool isError() const; const std::string &getMessage() const; @@ -43,10 +44,27 @@ class Error final void createMessageString() const; GLenum mCode; - mutable std::string *mMessage; + GLuint mID; + mutable std::unique_ptr mMessage; }; -} +template +class ErrorOrResult +{ + public: + ErrorOrResult(const gl::Error &error) : mError(error) {} + ErrorOrResult(T &&result) : mError(GL_NO_ERROR), mResult(std::move(result)) {} + + bool isError() const { return mError.isError(); } + const gl::Error &getError() const { return mError; } + T &&getResult() { return std::move(mResult); } + + private: + Error mError; + T mResult; +}; + +} // namespace gl namespace egl { @@ -60,8 +78,6 @@ class Error final inline Error(const Error &other); inline Error(Error &&other); - inline ~Error(); - inline Error &operator=(const Error &other); inline Error &operator=(Error &&other); @@ -76,10 +92,10 @@ class Error final EGLint mCode; EGLint mID; - mutable std::string *mMessage; + mutable std::unique_ptr mMessage; }; -} +} // namespace egl #include "Error.inl" diff --git a/gfx/angle/src/libANGLE/Error.inl b/gfx/angle/src/libANGLE/Error.inl index 32e8f05828..900fc5fd03 100644 --- a/gfx/angle/src/libANGLE/Error.inl +++ b/gfx/angle/src/libANGLE/Error.inl @@ -15,15 +15,15 @@ namespace gl Error::Error(GLenum errorCode) : mCode(errorCode), - mMessage(nullptr) + mID(errorCode) { } Error::Error(const Error &other) : mCode(other.mCode), - mMessage(nullptr) + mID(other.mID) { - if (other.mMessage != nullptr) + if (other.mMessage) { createMessageString(); *mMessage = *(other.mMessage); @@ -32,28 +32,24 @@ Error::Error(const Error &other) Error::Error(Error &&other) : mCode(other.mCode), - mMessage(other.mMessage) + mID(other.mID), + mMessage(std::move(other.mMessage)) { - other.mMessage = nullptr; -} - -Error::~Error() -{ - SafeDelete(mMessage); } Error &Error::operator=(const Error &other) { mCode = other.mCode; + mID = other.mID; - if (other.mMessage != nullptr) + if (other.mMessage) { createMessageString(); *mMessage = *(other.mMessage); } else { - SafeDelete(mMessage); + mMessage.release(); } return *this; @@ -61,10 +57,12 @@ Error &Error::operator=(const Error &other) Error &Error::operator=(Error &&other) { - mCode = other.mCode; - mMessage = other.mMessage; - - other.mMessage = nullptr; + if (this != &other) + { + mCode = other.mCode; + mID = other.mID; + mMessage = std::move(other.mMessage); + } return *this; } @@ -74,6 +72,11 @@ GLenum Error::getCode() const return mCode; } +GLuint Error::getID() const +{ + return mID; +} + bool Error::isError() const { return (mCode != GL_NO_ERROR); @@ -86,17 +89,15 @@ namespace egl Error::Error(EGLint errorCode) : mCode(errorCode), - mID(0), - mMessage(nullptr) + mID(0) { } Error::Error(const Error &other) : mCode(other.mCode), - mID(other.mID), - mMessage(nullptr) + mID(other.mID) { - if (other.mMessage != nullptr) + if (other.mMessage) { createMessageString(); *mMessage = *(other.mMessage); @@ -106,14 +107,8 @@ Error::Error(const Error &other) Error::Error(Error &&other) : mCode(other.mCode), mID(other.mID), - mMessage(other.mMessage) + mMessage(std::move(other.mMessage)) { - other.mMessage = nullptr; -} - -Error::~Error() -{ - SafeDelete(mMessage); } Error &Error::operator=(const Error &other) @@ -121,14 +116,14 @@ Error &Error::operator=(const Error &other) mCode = other.mCode; mID = other.mID; - if (other.mMessage != nullptr) + if (other.mMessage) { createMessageString(); *mMessage = *(other.mMessage); } else { - SafeDelete(mMessage); + mMessage.release(); } return *this; @@ -136,11 +131,12 @@ Error &Error::operator=(const Error &other) Error &Error::operator=(Error &&other) { - mCode = other.mCode; - mID = other.mID; - mMessage = other.mMessage; - - other.mMessage = nullptr; + if (this != &other) + { + mCode = other.mCode; + mID = other.mID; + mMessage = std::move(other.mMessage); + } return *this; } diff --git a/gfx/angle/src/libANGLE/Fence.cpp b/gfx/angle/src/libANGLE/Fence.cpp index aa76791ee7..ff32f4bbe9 100644 --- a/gfx/angle/src/libANGLE/Fence.cpp +++ b/gfx/angle/src/libANGLE/Fence.cpp @@ -76,10 +76,7 @@ Error FenceNV::finish() } FenceSync::FenceSync(rx::FenceSyncImpl *impl, GLuint id) - : RefCountObject(id), - mFence(impl), - mCondition(GL_NONE), - mFlags(0) + : RefCountObject(id), mFence(impl), mLabel(), mCondition(GL_NONE), mFlags(0) { } @@ -88,6 +85,16 @@ FenceSync::~FenceSync() SafeDelete(mFence); } +void FenceSync::setLabel(const std::string &label) +{ + mLabel = label; +} + +const std::string &FenceSync::getLabel() const +{ + return mLabel; +} + Error FenceSync::set(GLenum condition, GLbitfield flags) { Error error = mFence->set(condition, flags); diff --git a/gfx/angle/src/libANGLE/Fence.h b/gfx/angle/src/libANGLE/Fence.h index 74dad0566e..b2daed6f0e 100644 --- a/gfx/angle/src/libANGLE/Fence.h +++ b/gfx/angle/src/libANGLE/Fence.h @@ -10,6 +10,7 @@ #ifndef LIBANGLE_FENCE_H_ #define LIBANGLE_FENCE_H_ +#include "libANGLE/Debug.h" #include "libANGLE/Error.h" #include "libANGLE/RefCountObject.h" @@ -47,12 +48,15 @@ class FenceNV final : angle::NonCopyable GLenum mCondition; }; -class FenceSync final : public RefCountObject +class FenceSync final : public RefCountObject, public LabeledObject { public: - explicit FenceSync(rx::FenceSyncImpl *impl, GLuint id); + FenceSync(rx::FenceSyncImpl *impl, GLuint id); virtual ~FenceSync(); + void setLabel(const std::string &label) override; + const std::string &getLabel() const override; + Error set(GLenum condition, GLbitfield flags); Error clientWait(GLbitfield flags, GLuint64 timeout, GLenum *outResult); Error serverWait(GLbitfield flags, GLuint64 timeout); @@ -64,6 +68,8 @@ class FenceSync final : public RefCountObject private: rx::FenceSyncImpl *mFence; + std::string mLabel; + GLenum mCondition; GLbitfield mFlags; }; diff --git a/gfx/angle/src/libANGLE/Framebuffer.cpp b/gfx/angle/src/libANGLE/Framebuffer.cpp index 731689ca72..3def57b87e 100644 --- a/gfx/angle/src/libANGLE/Framebuffer.cpp +++ b/gfx/angle/src/libANGLE/Framebuffer.cpp @@ -9,6 +9,7 @@ #include "libANGLE/Framebuffer.h" +#include "common/Optional.h" #include "common/utilities.h" #include "libANGLE/Config.h" #include "libANGLE/Context.h" @@ -39,7 +40,8 @@ void DetachMatchingAttachment(FramebufferAttachment *attachment, GLenum matchTyp } Framebuffer::Data::Data() - : mColorAttachments(1), + : mLabel(), + mColorAttachments(1), mDrawBufferStates(1, GL_NONE), mReadBufferState(GL_COLOR_ATTACHMENT0_EXT) { @@ -47,10 +49,12 @@ Framebuffer::Data::Data() } Framebuffer::Data::Data(const Caps &caps) - : mColorAttachments(caps.maxColorAttachments), + : mLabel(), + mColorAttachments(caps.maxColorAttachments), mDrawBufferStates(caps.maxDrawBuffers, GL_NONE), mReadBufferState(GL_COLOR_ATTACHMENT0_EXT) { + ASSERT(mDrawBufferStates.size() > 0); mDrawBufferStates[0] = GL_COLOR_ATTACHMENT0_EXT; } @@ -58,6 +62,11 @@ Framebuffer::Data::~Data() { } +const std::string &Framebuffer::Data::getLabel() +{ + return mLabel; +} + const FramebufferAttachment *Framebuffer::Data::getReadAttachment() const { ASSERT(mReadBufferState == GL_BACK || (mReadBufferState >= GL_COLOR_ATTACHMENT0 && mReadBufferState <= GL_COLOR_ATTACHMENT15)); @@ -124,6 +133,42 @@ const FramebufferAttachment *Framebuffer::Data::getDepthStencilAttachment() cons return nullptr; } +bool Framebuffer::Data::attachmentsHaveSameDimensions() const +{ + Optional attachmentSize; + + auto hasMismatchedSize = [&attachmentSize](const FramebufferAttachment &attachment) + { + if (!attachment.isAttached()) + { + return false; + } + + if (!attachmentSize.valid()) + { + attachmentSize = attachment.getSize(); + return false; + } + + return (attachment.getSize() != attachmentSize.value()); + }; + + for (const auto &attachment : mColorAttachments) + { + if (hasMismatchedSize(attachment)) + { + return false; + } + } + + if (hasMismatchedSize(mDepthAttachment)) + { + return false; + } + + return !hasMismatchedSize(mStencilAttachment); +} + Framebuffer::Framebuffer(const Caps &caps, rx::ImplFactory *factory, GLuint id) : mData(caps), mImpl(factory->createFramebuffer(mData)), mId(id) { @@ -142,6 +187,16 @@ Framebuffer::~Framebuffer() SafeDelete(mImpl); } +void Framebuffer::setLabel(const std::string &label) +{ + mData.mLabel = label; +} + +const std::string &Framebuffer::getLabel() const +{ + return mData.mLabel; +} + void Framebuffer::detachTexture(GLuint textureId) { detachResourceById(GL_TEXTURE, textureId); @@ -233,10 +288,15 @@ const FramebufferAttachment *Framebuffer::getAttachment(GLenum attachment) const } } -GLenum Framebuffer::getDrawBufferState(unsigned int colorAttachment) const +size_t Framebuffer::getDrawbufferStateCount() const { - ASSERT(colorAttachment < mData.mDrawBufferStates.size()); - return mData.mDrawBufferStates[colorAttachment]; + return mData.mDrawBufferStates.size(); +} + +GLenum Framebuffer::getDrawBufferState(size_t drawBuffer) const +{ + ASSERT(drawBuffer < mData.mDrawBufferStates.size()); + return mData.mDrawBufferStates[drawBuffer]; } void Framebuffer::setDrawBuffers(size_t count, const GLenum *buffers) @@ -246,7 +306,37 @@ void Framebuffer::setDrawBuffers(size_t count, const GLenum *buffers) ASSERT(count <= drawStates.size()); std::copy(buffers, buffers + count, drawStates.begin()); std::fill(drawStates.begin() + count, drawStates.end(), GL_NONE); - mImpl->setDrawBuffers(count, buffers); + mDirtyBits.set(DIRTY_BIT_DRAW_BUFFERS); +} + +const FramebufferAttachment *Framebuffer::getDrawBuffer(size_t drawBuffer) const +{ + ASSERT(drawBuffer < mData.mDrawBufferStates.size()); + if (mData.mDrawBufferStates[drawBuffer] != GL_NONE) + { + // ES3 spec: "If the GL is bound to a draw framebuffer object, the ith buffer listed in bufs + // must be COLOR_ATTACHMENTi or NONE" + ASSERT(mData.mDrawBufferStates[drawBuffer] == GL_COLOR_ATTACHMENT0 + drawBuffer || + (drawBuffer == 0 && mData.mDrawBufferStates[drawBuffer] == GL_BACK)); + return getAttachment(mData.mDrawBufferStates[drawBuffer]); + } + else + { + return nullptr; + } +} + +bool Framebuffer::hasEnabledDrawBuffer() const +{ + for (size_t drawbufferIdx = 0; drawbufferIdx < mData.mDrawBufferStates.size(); ++drawbufferIdx) + { + if (getDrawBuffer(drawbufferIdx) != nullptr) + { + return true; + } + } + + return false; } GLenum Framebuffer::getReadBufferState() const @@ -260,27 +350,7 @@ void Framebuffer::setReadBuffer(GLenum buffer) (buffer >= GL_COLOR_ATTACHMENT0 && (buffer - GL_COLOR_ATTACHMENT0) < mData.mColorAttachments.size())); mData.mReadBufferState = buffer; - mImpl->setReadBuffer(buffer); -} - -bool Framebuffer::isEnabledColorAttachment(size_t colorAttachment) const -{ - ASSERT(colorAttachment < mData.mColorAttachments.size()); - return (mData.mColorAttachments[colorAttachment].isAttached() && - mData.mDrawBufferStates[colorAttachment] != GL_NONE); -} - -bool Framebuffer::hasEnabledColorAttachment() const -{ - for (size_t colorAttachment = 0; colorAttachment < mData.mColorAttachments.size(); ++colorAttachment) - { - if (isEnabledColorAttachment(static_cast(colorAttachment))) - { - return true; - } - } - - return false; + mDirtyBits.set(DIRTY_BIT_READ_BUFFER); } size_t Framebuffer::getNumColorBuffers() const @@ -288,6 +358,11 @@ size_t Framebuffer::getNumColorBuffers() const return mData.mColorAttachments.size(); } +bool Framebuffer::hasDepth() const +{ + return (mData.mDepthAttachment.isAttached() && mData.mDepthAttachment.getDepthSize() > 0); +} + bool Framebuffer::hasStencil() const { return (mData.mStencilAttachment.isAttached() && mData.mStencilAttachment.getStencilSize() > 0); @@ -295,9 +370,9 @@ bool Framebuffer::hasStencil() const bool Framebuffer::usingExtendedDrawBuffers() const { - for (size_t colorAttachment = 1; colorAttachment < mData.mColorAttachments.size(); ++colorAttachment) + for (size_t drawbufferIdx = 1; drawbufferIdx < mData.mDrawBufferStates.size(); ++drawbufferIdx) { - if (isEnabledColorAttachment(static_cast(colorAttachment))) + if (getDrawBuffer(drawbufferIdx) != nullptr) { return true; } @@ -315,8 +390,6 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const return GL_FRAMEBUFFER_COMPLETE; } - int width = 0; - int height = 0; unsigned int colorbufferSize = 0; int samples = -1; bool missingAttachment = true; @@ -325,7 +398,8 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const { if (colorAttachment.isAttached()) { - if (colorAttachment.getWidth() == 0 || colorAttachment.getHeight() == 0) + const Extents &size = colorAttachment.getSize(); + if (size.width == 0 || size.height == 0) { return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; } @@ -337,13 +411,29 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const { if (!formatCaps.renderable) { - return GL_FRAMEBUFFER_UNSUPPORTED; + return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; } if (formatInfo.depthBits > 0 || formatInfo.stencilBits > 0) { return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; } + + if (colorAttachment.layer() >= size.depth) + { + return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + } + + // ES3 specifies that cube map texture attachments must be cube complete. + // This language is missing from the ES2 spec, but we enforce it here because some + // desktop OpenGL drivers also enforce this validation. + // TODO(jmadill): Check if OpenGL ES2 drivers enforce cube completeness. + const Texture *texture = colorAttachment.getTexture(); + ASSERT(texture); + if (texture->getTarget() == GL_TEXTURE_CUBE_MAP && !texture->isCubeComplete()) + { + return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + } } else if (colorAttachment.type() == GL_RENDERBUFFER) { @@ -355,12 +445,6 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const if (!missingAttachment) { - // all color attachments must have the same width and height - if (colorAttachment.getWidth() != width || colorAttachment.getHeight() != height) - { - return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; - } - // APPLE_framebuffer_multisample, which EXT_draw_buffers refers to, requires that // all color attachments have the same number of samples for the FBO to be complete. if (colorAttachment.getSamples() != samples) @@ -380,8 +464,6 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const } else { - width = colorAttachment.getWidth(); - height = colorAttachment.getHeight(); samples = colorAttachment.getSamples(); colorbufferSize = formatInfo.pixelBytes; missingAttachment = false; @@ -392,7 +474,8 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const const FramebufferAttachment &depthAttachment = mData.mDepthAttachment; if (depthAttachment.isAttached()) { - if (depthAttachment.getWidth() == 0 || depthAttachment.getHeight() == 0) + const Extents &size = depthAttachment.getSize(); + if (size.width == 0 || size.height == 0) { return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; } @@ -410,7 +493,7 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const if (!formatCaps.renderable) { - return GL_FRAMEBUFFER_UNSUPPORTED; + return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; } if (formatInfo.depthBits == 0) @@ -428,15 +511,9 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const if (missingAttachment) { - width = depthAttachment.getWidth(); - height = depthAttachment.getHeight(); samples = depthAttachment.getSamples(); missingAttachment = false; } - else if (width != depthAttachment.getWidth() || height != depthAttachment.getHeight()) - { - return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; - } else if (samples != depthAttachment.getSamples()) { return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE; @@ -446,7 +523,8 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const const FramebufferAttachment &stencilAttachment = mData.mStencilAttachment; if (stencilAttachment.isAttached()) { - if (stencilAttachment.getWidth() == 0 || stencilAttachment.getHeight() == 0) + const Extents &size = stencilAttachment.getSize(); + if (size.width == 0 || size.height == 0) { return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; } @@ -465,7 +543,7 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const if (!formatCaps.renderable) { - return GL_FRAMEBUFFER_UNSUPPORTED; + return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; } if (formatInfo.stencilBits == 0) @@ -483,15 +561,9 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const if (missingAttachment) { - width = stencilAttachment.getWidth(); - height = stencilAttachment.getHeight(); samples = stencilAttachment.getSamples(); missingAttachment = false; } - else if (width != stencilAttachment.getWidth() || height != stencilAttachment.getHeight()) - { - return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; - } else if (samples != stencilAttachment.getSamples()) { return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE; @@ -504,7 +576,20 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; } - return mImpl->checkStatus(); + // In ES 2.0, all color attachments must have the same width and height. + // In ES 3.0, there is no such restriction. + if (data.clientVersion < 3 && !mData.attachmentsHaveSameDimensions()) + { + return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; + } + + syncState(); + if (!mImpl->checkStatus()) + { + return GL_FRAMEBUFFER_UNSUPPORTED; + } + + return GL_FRAMEBUFFER_COMPLETE; } Error Framebuffer::discard(size_t count, const GLenum *attachments) @@ -522,57 +607,67 @@ Error Framebuffer::invalidateSub(size_t count, const GLenum *attachments, const return mImpl->invalidateSub(count, attachments, area); } -Error Framebuffer::clear(Context *context, GLbitfield mask) +Error Framebuffer::clear(const gl::Data &data, GLbitfield mask) { - // Sync the clear state - context->syncRendererState(context->getState().clearStateBitMask()); + if (data.state->isRasterizerDiscardEnabled()) + { + return gl::Error(GL_NO_ERROR); + } - return mImpl->clear(context->getData(), mask); + return mImpl->clear(data, mask); } -Error Framebuffer::clearBufferfv(Context *context, +Error Framebuffer::clearBufferfv(const gl::Data &data, GLenum buffer, GLint drawbuffer, const GLfloat *values) { - // Sync the clear state - context->syncRendererState(context->getState().clearStateBitMask()); + if (data.state->isRasterizerDiscardEnabled()) + { + return gl::Error(GL_NO_ERROR); + } - return mImpl->clearBufferfv(context->getState(), buffer, drawbuffer, values); + return mImpl->clearBufferfv(data, buffer, drawbuffer, values); } -Error Framebuffer::clearBufferuiv(Context *context, +Error Framebuffer::clearBufferuiv(const gl::Data &data, GLenum buffer, GLint drawbuffer, const GLuint *values) { - // Sync the clear state - context->syncRendererState(context->getState().clearStateBitMask()); + if (data.state->isRasterizerDiscardEnabled()) + { + return gl::Error(GL_NO_ERROR); + } - return mImpl->clearBufferuiv(context->getState(), buffer, drawbuffer, values); + return mImpl->clearBufferuiv(data, buffer, drawbuffer, values); } -Error Framebuffer::clearBufferiv(Context *context, +Error Framebuffer::clearBufferiv(const gl::Data &data, GLenum buffer, GLint drawbuffer, const GLint *values) { - // Sync the clear state - context->syncRendererState(context->getState().clearStateBitMask()); + if (data.state->isRasterizerDiscardEnabled()) + { + return gl::Error(GL_NO_ERROR); + } - return mImpl->clearBufferiv(context->getState(), buffer, drawbuffer, values); + return mImpl->clearBufferiv(data, buffer, drawbuffer, values); } -Error Framebuffer::clearBufferfi(Context *context, +Error Framebuffer::clearBufferfi(const gl::Data &data, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { - // Sync the clear state - context->syncRendererState(context->getState().clearStateBitMask()); + if (data.state->isRasterizerDiscardEnabled()) + { + return gl::Error(GL_NO_ERROR); + } - return mImpl->clearBufferfi(context->getState(), buffer, drawbuffer, depth, stencil); + return mImpl->clearBufferfi(data, buffer, drawbuffer, depth, stencil); } GLenum Framebuffer::getImplementationColorReadFormat() const @@ -585,17 +680,12 @@ GLenum Framebuffer::getImplementationColorReadType() const return mImpl->getImplementationColorReadType(); } -Error Framebuffer::readPixels(Context *context, - const gl::Rectangle &area, +Error Framebuffer::readPixels(const State &state, + const Rectangle &area, GLenum format, GLenum type, GLvoid *pixels) const { - const State &state = context->getState(); - - // Sync pack state - context->syncRendererState(state.packStateBitMask()); - Error error = mImpl->readPixels(state, area, format, type, pixels); if (error.isError()) { @@ -611,17 +701,13 @@ Error Framebuffer::readPixels(Context *context, return Error(GL_NO_ERROR); } -Error Framebuffer::blit(Context *context, - const gl::Rectangle &sourceArea, - const gl::Rectangle &destArea, +Error Framebuffer::blit(const State &state, + const Rectangle &sourceArea, + const Rectangle &destArea, GLbitfield mask, GLenum filter, - const gl::Framebuffer *sourceFramebuffer) + const Framebuffer *sourceFramebuffer) { - // Sync blit state - const State &state = context->getState(); - context->syncRendererState(state.blitStateBitMask()); - return mImpl->blit(state, sourceArea, destArea, mask, filter, sourceFramebuffer); } @@ -671,32 +757,33 @@ void Framebuffer::setAttachment(GLenum type, mData.mDepthAttachment.attach(type, binding, textureIndex, attachmentObj); mData.mStencilAttachment.attach(type, binding, textureIndex, attachmentObj); - mImpl->onUpdateDepthStencilAttachment(); + mDirtyBits.set(DIRTY_BIT_DEPTH_ATTACHMENT); + mDirtyBits.set(DIRTY_BIT_STENCIL_ATTACHMENT); } else { switch (binding) { - case GL_DEPTH: - case GL_DEPTH_ATTACHMENT: - mData.mDepthAttachment.attach(type, binding, textureIndex, resource); - mImpl->onUpdateDepthAttachment(); + case GL_DEPTH: + case GL_DEPTH_ATTACHMENT: + mData.mDepthAttachment.attach(type, binding, textureIndex, resource); + mDirtyBits.set(DIRTY_BIT_DEPTH_ATTACHMENT); break; - case GL_STENCIL: - case GL_STENCIL_ATTACHMENT: - mData.mStencilAttachment.attach(type, binding, textureIndex, resource); - mImpl->onUpdateStencilAttachment(); + case GL_STENCIL: + case GL_STENCIL_ATTACHMENT: + mData.mStencilAttachment.attach(type, binding, textureIndex, resource); + mDirtyBits.set(DIRTY_BIT_STENCIL_ATTACHMENT); break; - case GL_BACK: - mData.mColorAttachments[0].attach(type, binding, textureIndex, resource); - mImpl->onUpdateColorAttachment(0); + case GL_BACK: + mData.mColorAttachments[0].attach(type, binding, textureIndex, resource); + mDirtyBits.set(DIRTY_BIT_COLOR_ATTACHMENT_0); break; - default: + default: { size_t colorIndex = binding - GL_COLOR_ATTACHMENT0; ASSERT(colorIndex < mData.mColorAttachments.size()); mData.mColorAttachments[colorIndex].attach(type, binding, textureIndex, resource); - mImpl->onUpdateColorAttachment(colorIndex); + mDirtyBits.set(DIRTY_BIT_COLOR_ATTACHMENT_0 + colorIndex); } break; } @@ -708,4 +795,13 @@ void Framebuffer::resetAttachment(GLenum binding) setAttachment(GL_NONE, binding, ImageIndex::MakeInvalid(), nullptr); } +void Framebuffer::syncState() const +{ + if (mDirtyBits.any()) + { + mImpl->syncState(mDirtyBits); + mDirtyBits.reset(); + } } + +} // namespace gl diff --git a/gfx/angle/src/libANGLE/Framebuffer.h b/gfx/angle/src/libANGLE/Framebuffer.h index 84e57b2ee8..b07b59ac90 100644 --- a/gfx/angle/src/libANGLE/Framebuffer.h +++ b/gfx/angle/src/libANGLE/Framebuffer.h @@ -14,6 +14,7 @@ #include "common/angleutils.h" #include "libANGLE/Constants.h" +#include "libANGLE/Debug.h" #include "libANGLE/Error.h" #include "libANGLE/FramebufferAttachment.h" #include "libANGLE/RefCountObject.h" @@ -44,7 +45,7 @@ struct Extensions; struct ImageIndex; struct Rectangle; -class Framebuffer +class Framebuffer final : public LabeledObject { public: @@ -55,6 +56,8 @@ class Framebuffer explicit Data(const Caps &caps); ~Data(); + const std::string &getLabel(); + const FramebufferAttachment *getReadAttachment() const; const FramebufferAttachment *getFirstColorAttachment() const; const FramebufferAttachment *getDepthOrStencilAttachment() const; @@ -64,11 +67,16 @@ class Framebuffer const FramebufferAttachment *getDepthStencilAttachment() const; const std::vector &getDrawBufferStates() const { return mDrawBufferStates; } + GLenum getReadBufferState() const { return mReadBufferState; } const std::vector &getColorAttachments() const { return mColorAttachments; } + bool attachmentsHaveSameDimensions() const; + private: friend class Framebuffer; + std::string mLabel; + std::vector mColorAttachments; FramebufferAttachment mDepthAttachment; FramebufferAttachment mStencilAttachment; @@ -81,6 +89,9 @@ class Framebuffer Framebuffer(rx::SurfaceImpl *surface); virtual ~Framebuffer(); + void setLabel(const std::string &label) override; + const std::string &getLabel() const override; + const rx::FramebufferImpl *getImplementation() const { return mImpl; } rx::FramebufferImpl *getImplementation() { return mImpl; } @@ -106,15 +117,17 @@ class Framebuffer const FramebufferAttachment *getAttachment(GLenum attachment) const; - GLenum getDrawBufferState(unsigned int colorAttachment) const; + size_t getDrawbufferStateCount() const; + GLenum getDrawBufferState(size_t drawBuffer) const; void setDrawBuffers(size_t count, const GLenum *buffers); + const FramebufferAttachment *getDrawBuffer(size_t drawBuffer) const; + bool hasEnabledDrawBuffer() const; GLenum getReadBufferState() const; void setReadBuffer(GLenum buffer); - bool isEnabledColorAttachment(size_t colorAttachment) const; - bool hasEnabledColorAttachment() const; size_t getNumColorBuffers() const; + bool hasDepth() const; bool hasStencil() const; int getSamples(const gl::Data &data) const; bool usingExtendedDrawBuffers() const; @@ -126,11 +139,17 @@ class Framebuffer Error invalidate(size_t count, const GLenum *attachments); Error invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area); - Error clear(Context *context, GLbitfield mask); - Error clearBufferfv(Context *context, GLenum buffer, GLint drawbuffer, const GLfloat *values); - Error clearBufferuiv(Context *context, GLenum buffer, GLint drawbuffer, const GLuint *values); - Error clearBufferiv(Context *context, GLenum buffer, GLint drawbuffer, const GLint *values); - Error clearBufferfi(Context *context, + Error clear(const gl::Data &data, GLbitfield mask); + Error clearBufferfv(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + const GLfloat *values); + Error clearBufferuiv(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + const GLuint *values); + Error clearBufferiv(const gl::Data &data, GLenum buffer, GLint drawbuffer, const GLint *values); + Error clearBufferfi(const gl::Data &data, GLenum buffer, GLint drawbuffer, GLfloat depth, @@ -138,18 +157,36 @@ class Framebuffer GLenum getImplementationColorReadFormat() const; GLenum getImplementationColorReadType() const; - Error readPixels(Context *context, + Error readPixels(const gl::State &state, const gl::Rectangle &area, GLenum format, GLenum type, GLvoid *pixels) const; - Error blit(Context *context, - const gl::Rectangle &sourceArea, - const gl::Rectangle &destArea, + Error blit(const State &state, + const Rectangle &sourceArea, + const Rectangle &destArea, GLbitfield mask, GLenum filter, - const gl::Framebuffer *sourceFramebuffer); + const Framebuffer *sourceFramebuffer); + + enum DirtyBitType + { + DIRTY_BIT_COLOR_ATTACHMENT_0, + DIRTY_BIT_COLOR_ATTACHMENT_MAX = + DIRTY_BIT_COLOR_ATTACHMENT_0 + gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS, + DIRTY_BIT_DEPTH_ATTACHMENT = DIRTY_BIT_COLOR_ATTACHMENT_MAX, + DIRTY_BIT_STENCIL_ATTACHMENT, + DIRTY_BIT_DRAW_BUFFERS, + DIRTY_BIT_READ_BUFFER, + DIRTY_BIT_UNKNOWN, + DIRTY_BIT_MAX = DIRTY_BIT_UNKNOWN, + }; + + typedef std::bitset DirtyBits; + bool hasAnyDirtyBit() const { return mDirtyBits.any(); } + + void syncState() const; protected: void detachResourceById(GLenum resourceType, GLuint resourceId); @@ -157,6 +194,9 @@ class Framebuffer Data mData; rx::FramebufferImpl *mImpl; GLuint mId; + + // TODO(jmadill): See if we can make this non-mutable. + mutable DirtyBits mDirtyBits; }; } diff --git a/gfx/angle/src/libANGLE/FramebufferAttachment.h b/gfx/angle/src/libANGLE/FramebufferAttachment.h index c4586ed097..33196f5c61 100644 --- a/gfx/angle/src/libANGLE/FramebufferAttachment.h +++ b/gfx/angle/src/libANGLE/FramebufferAttachment.h @@ -12,6 +12,7 @@ #include "angle_gl.h" #include "common/angleutils.h" +#include "libANGLE/angletypes.h" #include "libANGLE/Error.h" #include "libANGLE/ImageIndex.h" @@ -111,8 +112,10 @@ class FramebufferAttachment final GLint mipLevel() const; GLint layer() const; - GLsizei getWidth() const; - GLsizei getHeight() const; + // The size of the underlying resource the attachment points to. The 'depth' value will + // correspond to a 3D texture depth or the layer count of a 2D array texture. For Surfaces and + // Renderbuffers, it will always be 1. + Extents getSize() const; GLenum getInternalFormat() const; GLsizei getSamples() const; GLenum type() const { return mType; } @@ -148,8 +151,7 @@ class FramebufferAttachmentObject FramebufferAttachmentObject() {} virtual ~FramebufferAttachmentObject() {} - virtual GLsizei getAttachmentWidth(const FramebufferAttachment::Target &target) const = 0; - virtual GLsizei getAttachmentHeight(const FramebufferAttachment::Target &target) const = 0; + virtual Extents getAttachmentSize(const FramebufferAttachment::Target &target) const = 0; virtual GLenum getAttachmentInternalFormat(const FramebufferAttachment::Target &target) const = 0; virtual GLsizei getAttachmentSamples(const FramebufferAttachment::Target &target) const = 0; @@ -164,14 +166,9 @@ class FramebufferAttachmentObject virtual rx::FramebufferAttachmentObjectImpl *getAttachmentImpl() const = 0; }; -inline GLsizei FramebufferAttachment::getWidth() const +inline Extents FramebufferAttachment::getSize() const { - return mResource->getAttachmentWidth(mTarget); -} - -inline GLsizei FramebufferAttachment::getHeight() const -{ - return mResource->getAttachmentHeight(mTarget); + return mResource->getAttachmentSize(mTarget); } inline GLenum FramebufferAttachment::getInternalFormat() const diff --git a/gfx/angle/src/libANGLE/HandleAllocator.cpp b/gfx/angle/src/libANGLE/HandleAllocator.cpp index 16365dc992..4815855d5a 100644 --- a/gfx/angle/src/libANGLE/HandleAllocator.cpp +++ b/gfx/angle/src/libANGLE/HandleAllocator.cpp @@ -126,8 +126,9 @@ void HandleAllocator::reserve(GLuint handle) } if (begin != handle) { + ASSERT(begin < handle); mUnallocatedList.insert(placementIt, HandleRange(begin, handle)); } } -} +} // namespace gl diff --git a/gfx/angle/src/libANGLE/HandleAllocator.h b/gfx/angle/src/libANGLE/HandleAllocator.h index c22f2ba61a..1888d57cfa 100644 --- a/gfx/angle/src/libANGLE/HandleAllocator.h +++ b/gfx/angle/src/libANGLE/HandleAllocator.h @@ -58,6 +58,6 @@ class HandleAllocator final : angle::NonCopyable std::vector mReleasedList; }; -} +} // namespace gl #endif // LIBANGLE_HANDLEALLOCATOR_H_ diff --git a/gfx/angle/src/libANGLE/Program.cpp b/gfx/angle/src/libANGLE/Program.cpp index 9347419db8..748ceae030 100644 --- a/gfx/angle/src/libANGLE/Program.cpp +++ b/gfx/angle/src/libANGLE/Program.cpp @@ -223,9 +223,11 @@ VariableLocation::VariableLocation(const std::string &name, unsigned int element } Program::Data::Data() - : mAttachedFragmentShader(nullptr), + : mLabel(), + mAttachedFragmentShader(nullptr), mAttachedVertexShader(nullptr), - mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS) + mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS), + mBinaryRetrieveableHint(false) { } @@ -242,6 +244,11 @@ Program::Data::~Data() } } +const std::string &Program::Data::getLabel() +{ + return mLabel; +} + const LinkedUniform *Program::Data::getUniformByName(const std::string &name) const { for (const LinkedUniform &linkedUniform : mUniforms) @@ -327,6 +334,16 @@ Program::~Program() SafeDelete(mProgram); } +void Program::setLabel(const std::string &label) +{ + mData.mLabel = label; +} + +const std::string &Program::getLabel() const +{ + return mData.mLabel; +} + bool Program::attachShader(Shader *shader) { if (shader->getType() == GL_VERTEX_SHADER) @@ -791,6 +808,18 @@ GLint Program::getBinaryLength() const return length; } +void Program::setBinaryRetrievableHint(bool retrievable) +{ + // TODO(jmadill) : replace with dirty bits + mProgram->setBinaryRetrievableHint(retrievable); + mData.mBinaryRetrieveableHint = retrievable; +} + +bool Program::getBinaryRetrievableHint() const +{ + return mData.mBinaryRetrieveableHint; +} + void Program::release() { mRefCount--; @@ -2348,7 +2377,7 @@ void Program::defineUniformBlock(const sh::InterfaceBlock &interfaceBlock, GLenu // Track the first and last uniform index to determine the range of active uniforms in the // block. size_t firstBlockUniformIndex = mData.mUniforms.size(); - defineUniformBlockMembers(interfaceBlock.fields, "", blockIndex); + defineUniformBlockMembers(interfaceBlock.fields, interfaceBlock.fieldPrefix(), blockIndex); size_t lastBlockUniformIndex = mData.mUniforms.size(); std::vector blockUniformIndexes; diff --git a/gfx/angle/src/libANGLE/Program.h b/gfx/angle/src/libANGLE/Program.h index ac06ff2751..f885ad1694 100644 --- a/gfx/angle/src/libANGLE/Program.h +++ b/gfx/angle/src/libANGLE/Program.h @@ -24,6 +24,7 @@ #include "libANGLE/angletypes.h" #include "libANGLE/Constants.h" +#include "libANGLE/Debug.h" #include "libANGLE/Error.h" #include "libANGLE/RefCountObject.h" @@ -143,7 +144,7 @@ struct VariableLocation unsigned int index; }; -class Program : angle::NonCopyable +class Program final : angle::NonCopyable, public LabeledObject { public: class Data final : angle::NonCopyable @@ -152,6 +153,8 @@ class Program : angle::NonCopyable Data(); ~Data(); + const std::string &getLabel(); + const Shader *getAttachedVertexShader() const { return mAttachedVertexShader; } const Shader *getAttachedFragmentShader() const { return mAttachedFragmentShader; } const std::vector &getTransformFeedbackVaryingNames() const @@ -191,6 +194,8 @@ class Program : angle::NonCopyable private: friend class Program; + std::string mLabel; + Shader *mAttachedFragmentShader; Shader *mAttachedVertexShader; @@ -215,6 +220,8 @@ class Program : angle::NonCopyable // TODO(jmadill): use unordered/hash map when available std::map mOutputVariables; + + bool mBinaryRetrieveableHint; }; Program(rx::ImplFactory *factory, ResourceManager *manager, GLuint handle); @@ -222,6 +229,9 @@ class Program : angle::NonCopyable GLuint id() const { return mHandle; } + void setLabel(const std::string &label) override; + const std::string &getLabel() const override; + rx::ProgramImpl *getImplementation() { return mProgram; } const rx::ProgramImpl *getImplementation() const { return mProgram; } @@ -237,6 +247,8 @@ class Program : angle::NonCopyable Error loadBinary(GLenum binaryFormat, const void *binary, GLsizei length); Error saveBinary(GLenum *binaryFormat, void *binary, GLsizei bufSize, GLsizei *length) const; GLint getBinaryLength() const; + void setBinaryRetrievableHint(bool retrievable); + bool getBinaryRetrievableHint() const; int getInfoLogLength() const; void getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog) const; diff --git a/gfx/angle/src/libANGLE/Query.cpp b/gfx/angle/src/libANGLE/Query.cpp index e88ea2bb02..cd1fb5f4bc 100644 --- a/gfx/angle/src/libANGLE/Query.cpp +++ b/gfx/angle/src/libANGLE/Query.cpp @@ -11,9 +11,7 @@ namespace gl { -Query::Query(rx::QueryImpl *impl, GLuint id) - : RefCountObject(id), - mQuery(impl) +Query::Query(rx::QueryImpl *impl, GLuint id) : RefCountObject(id), mQuery(impl), mLabel() { } @@ -22,6 +20,16 @@ Query::~Query() SafeDelete(mQuery); } +void Query::setLabel(const std::string &label) +{ + mLabel = label; +} + +const std::string &Query::getLabel() const +{ + return mLabel; +} + Error Query::begin() { return mQuery->begin(); @@ -32,12 +40,32 @@ Error Query::end() return mQuery->end(); } +Error Query::queryCounter() +{ + return mQuery->queryCounter(); +} + +Error Query::getResult(GLint *params) +{ + return mQuery->getResult(params); +} + Error Query::getResult(GLuint *params) { return mQuery->getResult(params); } -Error Query::isResultAvailable(GLuint *available) +Error Query::getResult(GLint64 *params) +{ + return mQuery->getResult(params); +} + +Error Query::getResult(GLuint64 *params) +{ + return mQuery->getResult(params); +} + +Error Query::isResultAvailable(bool *available) { return mQuery->isResultAvailable(available); } diff --git a/gfx/angle/src/libANGLE/Query.h b/gfx/angle/src/libANGLE/Query.h index cf54199524..5486f983e7 100644 --- a/gfx/angle/src/libANGLE/Query.h +++ b/gfx/angle/src/libANGLE/Query.h @@ -9,6 +9,7 @@ #ifndef LIBANGLE_QUERY_H_ #define LIBANGLE_QUERY_H_ +#include "libANGLE/Debug.h" #include "libANGLE/Error.h" #include "libANGLE/RefCountObject.h" @@ -24,17 +25,23 @@ class QueryImpl; namespace gl { -class Query : public RefCountObject +class Query final : public RefCountObject, public LabeledObject { public: Query(rx::QueryImpl *impl, GLuint id); virtual ~Query(); + void setLabel(const std::string &label) override; + const std::string &getLabel() const override; + Error begin(); Error end(); - + Error queryCounter(); + Error getResult(GLint *params); Error getResult(GLuint *params); - Error isResultAvailable(GLuint *available); + Error getResult(GLint64 *params); + Error getResult(GLuint64 *params); + Error isResultAvailable(bool *available); GLenum getType() const; @@ -43,6 +50,8 @@ class Query : public RefCountObject private: rx::QueryImpl *mQuery; + + std::string mLabel; }; } diff --git a/gfx/angle/src/libANGLE/RefCountObject.cpp b/gfx/angle/src/libANGLE/RefCountObject.cpp deleted file mode 100644 index b1210200cf..0000000000 --- a/gfx/angle/src/libANGLE/RefCountObject.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// -// Copyright (c) 2002-2010 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. -// - -// RefCountObject.cpp: Defines the gl::RefCountObject base class that provides -// lifecycle support for GL objects using the traditional BindObject scheme, but -// that need to be reference counted for correct cross-context deletion. -// (Concretely, textures, buffers and renderbuffers.) - -#include "RefCountObject.h" - -RefCountObject::RefCountObject(GLuint id) - : mId(id), - mRefCount(0) -{ -} - -RefCountObject::~RefCountObject() -{ - ASSERT(mRefCount == 0); -} - -void RefCountObject::addRef() const -{ - mRefCount++; -} - -void RefCountObject::release() const -{ - ASSERT(mRefCount > 0); - - if (--mRefCount == 0) - { - delete this; - } -} - diff --git a/gfx/angle/src/libANGLE/RefCountObject.h b/gfx/angle/src/libANGLE/RefCountObject.h index 6250834ec5..86e6d788b5 100644 --- a/gfx/angle/src/libANGLE/RefCountObject.h +++ b/gfx/angle/src/libANGLE/RefCountObject.h @@ -21,16 +21,27 @@ class RefCountObject : angle::NonCopyable { public: - explicit RefCountObject(GLuint id); - virtual ~RefCountObject(); + explicit RefCountObject(GLuint id) : mId(id), mRefCount(0) {} - virtual void addRef() const; - virtual void release() const; + void addRef() const { ++mRefCount; } + + void release() const + { + ASSERT(mRefCount > 0); + + if (--mRefCount == 0) + { + delete this; + } + } GLuint id() const { return mId; } size_t getRefCount() const { return mRefCount; } + protected: + virtual ~RefCountObject() { ASSERT(mRefCount == 0); } + private: GLuint mId; diff --git a/gfx/angle/src/libANGLE/Renderbuffer.cpp b/gfx/angle/src/libANGLE/Renderbuffer.cpp index 27508acd57..161fbea797 100644 --- a/gfx/angle/src/libANGLE/Renderbuffer.cpp +++ b/gfx/angle/src/libANGLE/Renderbuffer.cpp @@ -22,6 +22,7 @@ namespace gl Renderbuffer::Renderbuffer(rx::RenderbufferImpl *impl, GLuint id) : egl::ImageSibling(id), mRenderbuffer(impl), + mLabel(), mWidth(0), mHeight(0), mInternalFormat(GL_RGBA4), @@ -34,6 +35,16 @@ Renderbuffer::~Renderbuffer() SafeDelete(mRenderbuffer); } +void Renderbuffer::setLabel(const std::string &label) +{ + mLabel = label; +} + +const std::string &Renderbuffer::getLabel() const +{ + return mLabel; +} + Error Renderbuffer::setStorage(GLenum internalformat, size_t width, size_t height) { orphanImages(); @@ -165,4 +176,9 @@ GLuint Renderbuffer::getId() const { return id(); } + +Extents Renderbuffer::getAttachmentSize(const FramebufferAttachment::Target & /*target*/) const +{ + return Extents(mWidth, mHeight, 1); +} } diff --git a/gfx/angle/src/libANGLE/Renderbuffer.h b/gfx/angle/src/libANGLE/Renderbuffer.h index 3f5247de94..04af03e879 100644 --- a/gfx/angle/src/libANGLE/Renderbuffer.h +++ b/gfx/angle/src/libANGLE/Renderbuffer.h @@ -13,6 +13,7 @@ #include "angle_gl.h" #include "common/angleutils.h" +#include "libANGLE/Debug.h" #include "libANGLE/Error.h" #include "libANGLE/FramebufferAttachment.h" #include "libANGLE/Image.h" @@ -25,12 +26,17 @@ namespace gl // FramebufferAttachment and Framebuffer for how they are applied to an FBO via an // attachment point. -class Renderbuffer : public egl::ImageSibling, public gl::FramebufferAttachmentObject +class Renderbuffer final : public egl::ImageSibling, + public gl::FramebufferAttachmentObject, + public LabeledObject { public: Renderbuffer(rx::RenderbufferImpl *impl, GLuint id); virtual ~Renderbuffer(); + void setLabel(const std::string &label) override; + const std::string &getLabel() const override; + Error setStorage(GLenum internalformat, size_t width, size_t height); Error setStorageMultisample(size_t samples, GLenum internalformat, size_t width, size_t height); Error setStorageEGLImageTarget(egl::Image *imageTarget); @@ -50,8 +56,7 @@ class Renderbuffer : public egl::ImageSibling, public gl::FramebufferAttachmentO GLuint getStencilSize() const; // FramebufferAttachmentObject Impl - GLsizei getAttachmentWidth(const FramebufferAttachment::Target &/*target*/) const override { return getWidth(); } - GLsizei getAttachmentHeight(const FramebufferAttachment::Target &/*target*/) const override { return getHeight(); } + Extents getAttachmentSize(const FramebufferAttachment::Target &target) const override; GLenum getAttachmentInternalFormat(const FramebufferAttachment::Target &/*target*/) const override { return getInternalFormat(); } GLsizei getAttachmentSamples(const FramebufferAttachment::Target &/*target*/) const override { return getSamples(); } @@ -64,6 +69,8 @@ class Renderbuffer : public egl::ImageSibling, public gl::FramebufferAttachmentO rx::RenderbufferImpl *mRenderbuffer; + std::string mLabel; + GLsizei mWidth; GLsizei mHeight; GLenum mInternalFormat; diff --git a/gfx/angle/src/libANGLE/Sampler.cpp b/gfx/angle/src/libANGLE/Sampler.cpp index eff8702007..d8d606a46f 100644 --- a/gfx/angle/src/libANGLE/Sampler.cpp +++ b/gfx/angle/src/libANGLE/Sampler.cpp @@ -16,7 +16,7 @@ namespace gl { Sampler::Sampler(rx::ImplFactory *factory, GLuint id) - : RefCountObject(id), mImpl(factory->createSampler()), mSamplerState() + : RefCountObject(id), mImpl(factory->createSampler()), mLabel(), mSamplerState() { } @@ -25,6 +25,16 @@ Sampler::~Sampler() SafeDelete(mImpl); } +void Sampler::setLabel(const std::string &label) +{ + mLabel = label; +} + +const std::string &Sampler::getLabel() const +{ + return mLabel; +} + void Sampler::setMinFilter(GLenum minFilter) { mSamplerState.minFilter = minFilter; diff --git a/gfx/angle/src/libANGLE/Sampler.h b/gfx/angle/src/libANGLE/Sampler.h index 2e78c58963..a40b1655fc 100644 --- a/gfx/angle/src/libANGLE/Sampler.h +++ b/gfx/angle/src/libANGLE/Sampler.h @@ -11,6 +11,7 @@ #define LIBANGLE_SAMPLER_H_ #include "libANGLE/angletypes.h" +#include "libANGLE/Debug.h" #include "libANGLE/RefCountObject.h" namespace rx @@ -22,12 +23,15 @@ class SamplerImpl; namespace gl { -class Sampler final : public RefCountObject +class Sampler final : public RefCountObject, public LabeledObject { public: Sampler(rx::ImplFactory *factory, GLuint id); ~Sampler() override; + void setLabel(const std::string &label) override; + const std::string &getLabel() const override; + void setMinFilter(GLenum minFilter); GLenum getMinFilter() const; @@ -66,6 +70,8 @@ class Sampler final : public RefCountObject private: rx::SamplerImpl *mImpl; + std::string mLabel; + SamplerState mSamplerState; }; diff --git a/gfx/angle/src/libANGLE/Shader.cpp b/gfx/angle/src/libANGLE/Shader.cpp index 6cfa4b9f1b..bbe9077fc9 100644 --- a/gfx/angle/src/libANGLE/Shader.cpp +++ b/gfx/angle/src/libANGLE/Shader.cpp @@ -72,7 +72,7 @@ bool CompareShaderVar(const sh::ShaderVariable &x, const sh::ShaderVariable &y) return gl::VariableSortOrder(x.type) < gl::VariableSortOrder(y.type); } -Shader::Data::Data(GLenum shaderType) : mShaderType(shaderType), mShaderVersion(100) +Shader::Data::Data(GLenum shaderType) : mLabel(), mShaderType(shaderType), mShaderVersion(100) { } @@ -103,6 +103,16 @@ Shader::~Shader() SafeDelete(mImplementation); } +void Shader::setLabel(const std::string &label) +{ + mData.mLabel = label; +} + +const std::string &Shader::getLabel() const +{ + return mData.mLabel; +} + GLuint Shader::getHandle() const { return mHandle; diff --git a/gfx/angle/src/libANGLE/Shader.h b/gfx/angle/src/libANGLE/Shader.h index 290e1cd6bf..997977c87b 100644 --- a/gfx/angle/src/libANGLE/Shader.h +++ b/gfx/angle/src/libANGLE/Shader.h @@ -21,6 +21,7 @@ #include "common/angleutils.h" #include "libANGLE/angletypes.h" +#include "libANGLE/Debug.h" namespace rx { @@ -36,7 +37,7 @@ struct Limitations; class ResourceManager; struct Data; -class Shader : angle::NonCopyable +class Shader final : angle::NonCopyable, public LabeledObject { public: class Data final : angle::NonCopyable @@ -45,6 +46,8 @@ class Shader : angle::NonCopyable Data(GLenum shaderType); ~Data(); + const std::string &getLabel() const { return mLabel; } + const std::string &getSource() const { return mSource; } const std::string &getTranslatedSource() const { return mTranslatedSource; } @@ -66,6 +69,8 @@ class Shader : angle::NonCopyable private: friend class Shader; + std::string mLabel; + GLenum mShaderType; int mShaderVersion; std::string mTranslatedSource; @@ -86,6 +91,9 @@ class Shader : angle::NonCopyable virtual ~Shader(); + void setLabel(const std::string &label) override; + const std::string &getLabel() const override; + GLenum getType() const { return mType; } GLuint getHandle() const; diff --git a/gfx/angle/src/libANGLE/State.cpp b/gfx/angle/src/libANGLE/State.cpp index 5f05f25155..a1437b838b 100644 --- a/gfx/angle/src/libANGLE/State.cpp +++ b/gfx/angle/src/libANGLE/State.cpp @@ -8,8 +8,10 @@ #include "libANGLE/State.h" +#include "common/BitSetIterator.h" #include "libANGLE/Context.h" #include "libANGLE/Caps.h" +#include "libANGLE/Debug.h" #include "libANGLE/Framebuffer.h" #include "libANGLE/FramebufferAttachment.h" #include "libANGLE/Query.h" @@ -78,7 +80,10 @@ State::~State() reset(); } -void State::initialize(const Caps &caps, GLuint clientVersion) +void State::initialize(const Caps &caps, + const Extensions &extensions, + GLuint clientVersion, + bool debug) { mMaxDrawBuffers = caps.maxDrawBuffers; mMaxCombinedTextureImageUnits = caps.maxCombinedTextureImageUnits; @@ -175,16 +180,20 @@ void State::initialize(const Caps &caps, GLuint clientVersion) mSamplers.resize(caps.maxCombinedTextureImageUnits); - mActiveQueries[GL_ANY_SAMPLES_PASSED].set(NULL); - mActiveQueries[GL_ANY_SAMPLES_PASSED_CONSERVATIVE].set(NULL); - mActiveQueries[GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN].set(NULL); + mActiveQueries[GL_ANY_SAMPLES_PASSED].set(nullptr); + mActiveQueries[GL_ANY_SAMPLES_PASSED_CONSERVATIVE].set(nullptr); + mActiveQueries[GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN].set(nullptr); + mActiveQueries[GL_TIME_ELAPSED_EXT].set(nullptr); - mProgram = NULL; + mProgram = nullptr; - mReadFramebuffer = NULL; - mDrawFramebuffer = NULL; + mReadFramebuffer = nullptr; + mDrawFramebuffer = nullptr; mPrimitiveRestart = false; + + mDebug.setOutputEnabled(debug); + mDebug.setMaxLoggedMessages(extensions.maxDebugLoggedMessages); } void State::reset() @@ -581,6 +590,12 @@ void State::setEnableFeature(GLenum feature, bool enabled) case GL_DITHER: setDither(enabled); break; case GL_PRIMITIVE_RESTART_FIXED_INDEX: setPrimitiveRestart(enabled); break; case GL_RASTERIZER_DISCARD: setRasterizerDiscard(enabled); break; + case GL_DEBUG_OUTPUT_SYNCHRONOUS: + mDebug.setOutputSynchronous(enabled); + break; + case GL_DEBUG_OUTPUT: + mDebug.setOutputEnabled(enabled); + break; default: UNREACHABLE(); } } @@ -600,6 +615,10 @@ bool State::getEnableFeature(GLenum feature) case GL_DITHER: return isDitherEnabled(); case GL_PRIMITIVE_RESTART_FIXED_INDEX: return isPrimitiveRestartEnabled(); case GL_RASTERIZER_DISCARD: return isRasterizerDiscardEnabled(); + case GL_DEBUG_OUTPUT_SYNCHRONOUS: + return mDebug.isOutputSynchronous(); + case GL_DEBUG_OUTPUT: + return mDebug.isOutputEnabled(); default: UNREACHABLE(); return false; } } @@ -659,6 +678,11 @@ void State::setSamplerTexture(GLenum type, Texture *texture) mSamplerTextures[type][mActiveSampler].set(texture); } +Texture *State::getTargetTexture(GLenum target) const +{ + return getSamplerTexture(static_cast(mActiveSampler), target); +} + Texture *State::getSamplerTexture(unsigned int sampler, GLenum type) const { const auto it = mSamplerTextures.find(type); @@ -812,22 +836,44 @@ void State::detachRenderbuffer(GLuint renderbuffer) void State::setReadFramebufferBinding(Framebuffer *framebuffer) { + if (mReadFramebuffer == framebuffer) + return; + mReadFramebuffer = framebuffer; + mDirtyBits.set(DIRTY_BIT_READ_FRAMEBUFFER_BINDING); + + if (mReadFramebuffer && mReadFramebuffer->hasAnyDirtyBit()) + { + mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER); + } } void State::setDrawFramebufferBinding(Framebuffer *framebuffer) { + if (mDrawFramebuffer == framebuffer) + return; + mDrawFramebuffer = framebuffer; + mDirtyBits.set(DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING); + + if (mDrawFramebuffer && mDrawFramebuffer->hasAnyDirtyBit()) + { + mDirtyObjects.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER); + } } Framebuffer *State::getTargetFramebuffer(GLenum target) const { switch (target) { - case GL_READ_FRAMEBUFFER_ANGLE: return mReadFramebuffer; - case GL_DRAW_FRAMEBUFFER_ANGLE: - case GL_FRAMEBUFFER: return mDrawFramebuffer; - default: UNREACHABLE(); return NULL; + case GL_READ_FRAMEBUFFER_ANGLE: + return mReadFramebuffer; + case GL_DRAW_FRAMEBUFFER_ANGLE: + case GL_FRAMEBUFFER: + return mDrawFramebuffer; + default: + UNREACHABLE(); + return NULL; } } @@ -856,7 +902,7 @@ bool State::removeReadFramebufferBinding(GLuint framebuffer) if (mReadFramebuffer != nullptr && mReadFramebuffer->id() == framebuffer) { - mReadFramebuffer = NULL; + setReadFramebufferBinding(nullptr); return true; } @@ -868,7 +914,7 @@ bool State::removeDrawFramebufferBinding(GLuint framebuffer) if (mReadFramebuffer != nullptr && mDrawFramebuffer->id() == framebuffer) { - mDrawFramebuffer = NULL; + setDrawFramebufferBinding(nullptr); return true; } @@ -879,7 +925,11 @@ void State::setVertexArrayBinding(VertexArray *vertexArray) { mVertexArray = vertexArray; mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_BINDING); - mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT); + + if (mVertexArray && mVertexArray->hasAnyDirtyBit()) + { + mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); + } } GLuint State::getVertexArrayId() const @@ -900,7 +950,7 @@ bool State::removeVertexArrayBinding(GLuint vertexArray) { mVertexArray = NULL; mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_BINDING); - mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT); + mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); return true; } @@ -956,10 +1006,22 @@ void State::detachTransformFeedback(GLuint transformFeedback) bool State::isQueryActive() const { - for (State::ActiveQueryMap::const_iterator i = mActiveQueries.begin(); - i != mActiveQueries.end(); i++) + for (auto &iter : mActiveQueries) { - if (i->second.get() != NULL) + if (iter.second.get() != NULL) + { + return true; + } + } + + return false; +} + +bool State::isQueryActive(Query *query) const +{ + for (auto &iter : mActiveQueries) + { + if (iter.second.get() == query) { return true; } @@ -999,17 +1061,6 @@ GLuint State::getArrayBufferId() const return mArrayBuffer.id(); } -bool State::removeArrayBufferBinding(GLuint buffer) -{ - if (mArrayBuffer.id() == buffer) - { - mArrayBuffer.set(NULL); - return true; - } - - return false; -} - void State::setGenericUniformBufferBinding(Buffer *buffer) { mGenericUniformBuffer.set(buffer); @@ -1062,10 +1113,32 @@ Buffer *State::getTargetBuffer(GLenum target) const } } +void State::detachBuffer(GLuint bufferName) +{ + BindingPointer *buffers[] = {&mArrayBuffer, &mCopyReadBuffer, + &mCopyWriteBuffer, &mPack.pixelBuffer, + &mUnpack.pixelBuffer, &mGenericUniformBuffer}; + for (auto buffer : buffers) + { + if (buffer->id() == bufferName) + { + buffer->set(nullptr); + } + } + + TransformFeedback *curTransformFeedback = getCurrentTransformFeedback(); + if (curTransformFeedback) + { + curTransformFeedback->detachBuffer(bufferName); + } + + getVertexArray()->detachBuffer(bufferName); +} + void State::setEnableVertexAttribArray(unsigned int attribNum, bool enabled) { getVertexArray()->enableAttribute(attribNum, enabled); - mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT); + mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); } void State::setVertexAttribf(GLuint index, const GLfloat values[4]) @@ -1099,13 +1172,13 @@ void State::setVertexAttribState(unsigned int attribNum, const void *pointer) { getVertexArray()->setAttributeState(attribNum, boundBuffer, size, type, normalized, pureInteger, stride, pointer); - mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT); + mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); } void State::setVertexAttribDivisor(GLuint index, GLuint divisor) { getVertexArray()->setVertexAttribDivisor(index, divisor); - mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT); + mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); } const VertexAttribCurrentValueData &State::getVertexAttribCurrentValue(unsigned int attribNum) const @@ -1260,6 +1333,16 @@ PixelUnpackState &State::getUnpackState() return mUnpack; } +const Debug &State::getDebug() const +{ + return mDebug; +} + +Debug &State::getDebug() +{ + return mDebug; +} + void State::getBooleanv(GLenum pname, GLboolean *params) { switch (pname) @@ -1286,6 +1369,15 @@ void State::getBooleanv(GLenum pname, GLboolean *params) case GL_PRIMITIVE_RESTART_FIXED_INDEX: *params = mPrimitiveRestart; break; + case GL_RASTERIZER_DISCARD: + *params = isRasterizerDiscardEnabled() ? GL_TRUE : GL_FALSE; + break; + case GL_DEBUG_OUTPUT_SYNCHRONOUS: + *params = mDebug.isOutputSynchronous() ? GL_TRUE : GL_FALSE; + break; + case GL_DEBUG_OUTPUT: + *params = mDebug.isOutputEnabled() ? GL_TRUE : GL_FALSE; + break; default: UNREACHABLE(); break; @@ -1526,7 +1618,7 @@ void State::getIntegerv(const gl::Data &data, GLenum pname, GLint *params) *params = mGenericUniformBuffer.id(); break; case GL_TRANSFORM_FEEDBACK_BINDING: - *params = mTransformFeedback->id(); + *params = mTransformFeedback.id(); break; case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: *params = mTransformFeedback->getGenericBuffer().id(); @@ -1543,12 +1635,37 @@ void State::getIntegerv(const gl::Data &data, GLenum pname, GLint *params) case GL_PIXEL_UNPACK_BUFFER_BINDING: *params = mUnpack.pixelBuffer.id(); break; + case GL_DEBUG_LOGGED_MESSAGES: + *params = static_cast(mDebug.getMessageCount()); + break; + case GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH: + *params = static_cast(mDebug.getNextMessageLength()); + break; + case GL_DEBUG_GROUP_STACK_DEPTH: + *params = static_cast(mDebug.getGroupStackDepth()); + break; default: UNREACHABLE(); break; } } +void State::getPointerv(GLenum pname, void **params) const +{ + switch (pname) + { + case GL_DEBUG_CALLBACK_FUNCTION: + *params = reinterpret_cast(mDebug.getCallback()); + break; + case GL_DEBUG_CALLBACK_USER_PARAM: + *params = const_cast(mDebug.getUserParam()); + break; + default: + UNREACHABLE(); + break; + } +} + bool State::getIndexedIntegerv(GLenum target, GLuint index, GLint *data) { switch (target) @@ -1633,4 +1750,92 @@ bool State::hasMappedBuffer(GLenum target) const } } +void State::syncDirtyObjects() +{ + if (!mDirtyObjects.any()) + return; + + syncDirtyObjects(mDirtyObjects); } + +void State::syncDirtyObjects(const DirtyObjects &bitset) +{ + for (auto dirtyObject : angle::IterateBitSet(bitset)) + { + switch (dirtyObject) + { + case DIRTY_OBJECT_READ_FRAMEBUFFER: + ASSERT(mReadFramebuffer); + mReadFramebuffer->syncState(); + break; + case DIRTY_OBJECT_DRAW_FRAMEBUFFER: + ASSERT(mDrawFramebuffer); + mDrawFramebuffer->syncState(); + break; + case DIRTY_OBJECT_VERTEX_ARRAY: + ASSERT(mVertexArray); + mVertexArray->syncImplState(); + break; + case DIRTY_OBJECT_PROGRAM: + // TODO(jmadill): implement this + break; + default: + UNREACHABLE(); + break; + } + } + + mDirtyObjects &= ~bitset; +} + +void State::syncDirtyObject(GLenum target) +{ + DirtyObjects localSet; + + switch (target) + { + case GL_READ_FRAMEBUFFER: + localSet.set(DIRTY_OBJECT_READ_FRAMEBUFFER); + break; + case GL_DRAW_FRAMEBUFFER: + localSet.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER); + break; + case GL_FRAMEBUFFER: + localSet.set(DIRTY_OBJECT_READ_FRAMEBUFFER); + localSet.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER); + break; + case GL_VERTEX_ARRAY: + localSet.set(DIRTY_OBJECT_VERTEX_ARRAY); + break; + case GL_PROGRAM: + localSet.set(DIRTY_OBJECT_PROGRAM); + break; + } + + syncDirtyObjects(localSet); +} + +void State::setObjectDirty(GLenum target) +{ + switch (target) + { + case GL_READ_FRAMEBUFFER: + mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER); + break; + case GL_DRAW_FRAMEBUFFER: + mDirtyObjects.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER); + break; + case GL_FRAMEBUFFER: + mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER); + mDirtyObjects.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER); + break; + case GL_VERTEX_ARRAY: + mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); + break; + case GL_PROGRAM: + mDirtyObjects.set(DIRTY_OBJECT_PROGRAM); + break; + } +} + +} // namespace gl diff --git a/gfx/angle/src/libANGLE/State.h b/gfx/angle/src/libANGLE/State.h index 64c3fa718e..e822d7e679 100644 --- a/gfx/angle/src/libANGLE/State.h +++ b/gfx/angle/src/libANGLE/State.h @@ -10,8 +10,10 @@ #define LIBANGLE_STATE_H_ #include +#include #include "common/angleutils.h" +#include "libANGLE/Debug.h" #include "libANGLE/Program.h" #include "libANGLE/RefCountObject.h" #include "libANGLE/Renderbuffer.h" @@ -29,7 +31,7 @@ class Context; struct Caps; struct Data; -typedef std::map< GLenum, BindingPointer > TextureMap; +typedef std::map> TextureMap; class State : angle::NonCopyable { @@ -37,7 +39,10 @@ class State : angle::NonCopyable State(); ~State(); - void initialize(const Caps& caps, GLuint clientVersion); + void initialize(const Caps &caps, + const Extensions &extensions, + GLuint clientVersion, + bool debug); void reset(); // State chunk getters @@ -144,6 +149,7 @@ class State : angle::NonCopyable void setActiveSampler(unsigned int active); unsigned int getActiveSampler() const; void setSamplerTexture(GLenum type, Texture *texture); + Texture *getTargetTexture(GLenum target) const; Texture *getSamplerTexture(unsigned int sampler, GLenum type) const; GLuint getSamplerTextureId(unsigned int sampler, GLenum type) const; void detachTexture(const TextureMap &zeroTextures, GLuint texture); @@ -190,6 +196,7 @@ class State : angle::NonCopyable // Query binding manipulation bool isQueryActive() const; + bool isQueryActive(Query *query) const; void setActiveQuery(GLenum target, Query *query); GLuint getActiveQueryId(GLenum target) const; Query *getActiveQuery(GLenum target) const; @@ -198,7 +205,6 @@ class State : angle::NonCopyable // GL_ARRAY_BUFFER void setArrayBufferBinding(Buffer *buffer); GLuint getArrayBufferId() const; - bool removeArrayBufferBinding(GLuint buffer); // GL_UNIFORM_BUFFER - Both indexed and generic targets void setGenericUniformBufferBinding(Buffer *buffer); @@ -215,6 +221,8 @@ class State : angle::NonCopyable // Retrieve typed buffer by target (non-indexed) Buffer *getTargetBuffer(GLenum target) const; + // Detach a buffer from all bindings + void detachBuffer(GLuint bufferName); // Vertex attrib manipulation void setEnableVertexAttribArray(unsigned int attribNum, bool enabled); @@ -257,10 +265,15 @@ class State : angle::NonCopyable const PixelUnpackState &getUnpackState() const; PixelUnpackState &getUnpackState(); + // Debug state + const Debug &getDebug() const; + Debug &getDebug(); + // State query functions void getBooleanv(GLenum pname, GLboolean *params); void getFloatv(GLenum pname, GLfloat *params); void getIntegerv(const gl::Data &data, GLenum pname, GLint *params); + void getPointerv(GLenum pname, void **params) const; bool getIndexedIntegerv(GLenum target, GLuint index, GLint *data); bool getIndexedInteger64v(GLenum target, GLuint index, GLint64 *data); @@ -316,26 +329,41 @@ class State : angle::NonCopyable DIRTY_BIT_GENERATE_MIPMAP_HINT, DIRTY_BIT_SHADER_DERIVATIVE_HINT, DIRTY_BIT_READ_FRAMEBUFFER_BINDING, - DIRTY_BIT_READ_FRAMEBUFFER_OBJECT, DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING, - DIRTY_BIT_DRAW_FRAMEBUFFER_OBJECT, DIRTY_BIT_RENDERBUFFER_BINDING, DIRTY_BIT_VERTEX_ARRAY_BINDING, - DIRTY_BIT_VERTEX_ARRAY_OBJECT, DIRTY_BIT_PROGRAM_BINDING, - DIRTY_BIT_PROGRAM_OBJECT, DIRTY_BIT_CURRENT_VALUE_0, DIRTY_BIT_CURRENT_VALUE_MAX = DIRTY_BIT_CURRENT_VALUE_0 + MAX_VERTEX_ATTRIBS, DIRTY_BIT_INVALID = DIRTY_BIT_CURRENT_VALUE_MAX, DIRTY_BIT_MAX = DIRTY_BIT_INVALID, }; + // TODO(jmadill): Consider storing dirty objects in a list instead of by binding. + enum DirtyObjectType + { + DIRTY_OBJECT_READ_FRAMEBUFFER, + DIRTY_OBJECT_DRAW_FRAMEBUFFER, + DIRTY_OBJECT_VERTEX_ARRAY, + DIRTY_OBJECT_PROGRAM, + DIRTY_OBJECT_UNKNOWN, + DIRTY_OBJECT_MAX = DIRTY_OBJECT_UNKNOWN, + }; + typedef std::bitset DirtyBits; const DirtyBits &getDirtyBits() const { return mDirtyBits; } void clearDirtyBits() { mDirtyBits.reset(); } void clearDirtyBits(const DirtyBits &bitset) { mDirtyBits &= ~bitset; } void setAllDirtyBits() { mDirtyBits.set(); } + typedef std::bitset DirtyObjects; + void clearDirtyObjects() { mDirtyObjects.reset(); } + void setAllDirtyObjects() { mDirtyObjects.set(); } + void syncDirtyObjects(); + void syncDirtyObjects(const DirtyObjects &bitset); + void syncDirtyObject(GLenum target); + void setObjectDirty(GLenum target); + // Dirty bit masks const DirtyBits &unpackStateBitMask() const { return mUnpackStateBitMask; } const DirtyBits &packStateBitMask() const { return mPackStateBitMask; } @@ -387,18 +415,18 @@ class State : angle::NonCopyable // Texture and sampler bindings size_t mActiveSampler; // Active texture unit selector - GL_TEXTURE0 - typedef std::vector< BindingPointer > TextureBindingVector; + typedef std::vector> TextureBindingVector; typedef std::map TextureBindingMap; TextureBindingMap mSamplerTextures; - typedef std::vector< BindingPointer > SamplerBindingVector; + typedef std::vector> SamplerBindingVector; SamplerBindingVector mSamplers; - typedef std::map< GLenum, BindingPointer > ActiveQueryMap; + typedef std::map> ActiveQueryMap; ActiveQueryMap mActiveQueries; BindingPointer mGenericUniformBuffer; - typedef std::vector< OffsetBindingPointer > BufferVector; + typedef std::vector> BufferVector; BufferVector mUniformBuffers; BindingPointer mTransformFeedback; @@ -411,11 +439,15 @@ class State : angle::NonCopyable bool mPrimitiveRestart; + Debug mDebug; + DirtyBits mDirtyBits; DirtyBits mUnpackStateBitMask; DirtyBits mPackStateBitMask; DirtyBits mClearStateBitMask; DirtyBits mBlitStateBitMask; + + DirtyObjects mDirtyObjects; }; } diff --git a/gfx/angle/src/libANGLE/Surface.cpp b/gfx/angle/src/libANGLE/Surface.cpp index 45d38ba950..b5ed0ff5a6 100644 --- a/gfx/angle/src/libANGLE/Surface.cpp +++ b/gfx/angle/src/libANGLE/Surface.cpp @@ -41,9 +41,15 @@ Surface::Surface(rx::SurfaceImpl *impl, // FIXME: Determine actual pixel aspect ratio mPixelAspectRatio(static_cast(1.0 * EGL_DISPLAY_SCALING)), mRenderBuffer(EGL_BACK_BUFFER), - mSwapBehavior(impl->getSwapBehavior()) + mSwapBehavior(impl->getSwapBehavior()), + mOrientation(0), + mTexture() { mPostSubBufferRequested = (attributes.get(EGL_POST_SUB_BUFFER_SUPPORTED_NV, EGL_FALSE) == EGL_TRUE); + mFlexibleSurfaceCompatibilityRequested = + (attributes.get(EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, EGL_FALSE) == EGL_TRUE); + + mDirectComposition = (attributes.get(EGL_DIRECT_COMPOSITION_ANGLE, EGL_FALSE) == EGL_TRUE); mFixedSize = (attributes.get(EGL_FIXED_SIZE_ANGLE, EGL_FALSE) == EGL_TRUE); if (mFixedSize) @@ -58,6 +64,8 @@ Surface::Surface(rx::SurfaceImpl *impl, mTextureTarget = attributes.get(EGL_TEXTURE_TARGET, EGL_NO_TEXTURE); } + mOrientation = attributes.get(EGL_SURFACE_ORIENTATION_ANGLE, 0); + mDefaultFramebuffer = createDefaultFramebuffer(); ASSERT(mDefaultFramebuffer != nullptr); } @@ -185,7 +193,7 @@ Error Surface::bindTexImage(gl::Texture *texture, EGLint buffer) texture->bindTexImageFromSurface(this); mTexture.set(texture); - return mImplementation->bindTexImage(buffer); + return mImplementation->bindTexImage(texture, buffer); } Error Surface::releaseTexImage(EGLint buffer) @@ -203,6 +211,11 @@ void Surface::releaseTexImageFromTexture() mTexture.set(nullptr); } +gl::Extents Surface::getAttachmentSize(const gl::FramebufferAttachment::Target & /*target*/) const +{ + return gl::Extents(getWidth(), getHeight(), 1); +} + GLenum Surface::getAttachmentInternalFormat(const gl::FramebufferAttachment::Target &target) const { const egl::Config *config = getConfig(); diff --git a/gfx/angle/src/libANGLE/Surface.h b/gfx/angle/src/libANGLE/Surface.h index cc6279fd1d..e110f5da7b 100644 --- a/gfx/angle/src/libANGLE/Surface.h +++ b/gfx/angle/src/libANGLE/Surface.h @@ -70,8 +70,7 @@ class Surface final : public gl::FramebufferAttachmentObject EGLint isFixedSize() const; // FramebufferAttachmentObject implementation - GLsizei getAttachmentWidth(const gl::FramebufferAttachment::Target &/*target*/) const override { return getWidth(); } - GLsizei getAttachmentHeight(const gl::FramebufferAttachment::Target &/*target*/) const override { return getHeight(); } + gl::Extents getAttachmentSize(const gl::FramebufferAttachment::Target &target) const override; GLenum getAttachmentInternalFormat(const gl::FramebufferAttachment::Target &target) const override; GLsizei getAttachmentSamples(const gl::FramebufferAttachment::Target &target) const override; @@ -79,6 +78,14 @@ class Surface final : public gl::FramebufferAttachmentObject void onDetach() override {} GLuint getId() const override; + bool flexibleSurfaceCompatibilityRequested() const + { + return mFlexibleSurfaceCompatibilityRequested; + } + EGLint getOrientation() const { return mOrientation; } + + bool directComposition() const { return mDirectComposition; } + private: virtual ~Surface(); rx::FramebufferAttachmentObjectImpl *getAttachmentImpl() const override { return mImplementation; } @@ -99,11 +106,14 @@ class Surface final : public gl::FramebufferAttachmentObject const egl::Config *mConfig; bool mPostSubBufferRequested; + bool mFlexibleSurfaceCompatibilityRequested; bool mFixedSize; size_t mFixedWidth; size_t mFixedHeight; + bool mDirectComposition; + EGLenum mTextureFormat; EGLenum mTextureTarget; @@ -111,6 +121,8 @@ class Surface final : public gl::FramebufferAttachmentObject EGLenum mRenderBuffer; // Render buffer EGLenum mSwapBehavior; // Buffer swap behavior + EGLint mOrientation; + BindingPointer mTexture; }; diff --git a/gfx/angle/src/libANGLE/Surface_unittest.cpp b/gfx/angle/src/libANGLE/Surface_unittest.cpp index cfa9cda212..2f40d7d3d3 100644 --- a/gfx/angle/src/libANGLE/Surface_unittest.cpp +++ b/gfx/angle/src/libANGLE/Surface_unittest.cpp @@ -31,7 +31,7 @@ class MockSurfaceImpl : public rx::SurfaceImpl MOCK_METHOD0(swap, egl::Error()); MOCK_METHOD4(postSubBuffer, egl::Error(EGLint, EGLint, EGLint, EGLint)); MOCK_METHOD2(querySurfacePointerANGLE, egl::Error(EGLint, void**)); - MOCK_METHOD1(bindTexImage, egl::Error(EGLint)); + MOCK_METHOD2(bindTexImage, egl::Error(gl::Texture*, EGLint)); MOCK_METHOD1(releaseTexImage, egl::Error(EGLint)); MOCK_METHOD1(setSwapInterval, void(EGLint)); MOCK_CONST_METHOD0(getWidth, EGLint()); @@ -51,10 +51,6 @@ TEST(SurfaceTest, DestructionDeletesImpl) EXPECT_CALL(*impl, getSwapBehavior()); EXPECT_CALL(*impl, createDefaultFramebuffer(testing::_)).WillOnce(testing::Return(framebuffer)); - EXPECT_CALL(*framebuffer, setDrawBuffers(1, testing::_)); - EXPECT_CALL(*framebuffer, setReadBuffer(GL_BACK)); - EXPECT_CALL(*framebuffer, onUpdateColorAttachment(0)); - egl::Config config; egl::Surface *surface = new egl::Surface(impl, EGL_WINDOW_BIT, &config, egl::AttributeMap()); diff --git a/gfx/angle/src/libANGLE/Texture.cpp b/gfx/angle/src/libANGLE/Texture.cpp index d5f5a19ad5..5ef6762d3d 100644 --- a/gfx/angle/src/libANGLE/Texture.cpp +++ b/gfx/angle/src/libANGLE/Texture.cpp @@ -50,6 +50,7 @@ static size_t GetImageDescIndex(GLenum target, size_t level) Texture::Texture(rx::TextureImpl *impl, GLuint id, GLenum target) : egl::ImageSibling(id), mTexture(impl), + mLabel(), mTextureState(), mTarget(target), mImageDescs(IMPLEMENTATION_MAX_TEXTURE_LEVELS * (target == GL_TEXTURE_CUBE_MAP ? 6 : 1)), @@ -68,6 +69,16 @@ Texture::~Texture() SafeDelete(mTexture); } +void Texture::setLabel(const std::string &label) +{ + mLabel = label; +} + +const std::string &Texture::getLabel() const +{ + return mLabel; +} + GLenum Texture::getTarget() const { return mTarget; @@ -801,19 +812,21 @@ bool Texture::computeLevelCompleteness(GLenum target, size_t level) const return false; } - if (levelImageDesc.size.width != std::max(1, baseImageDesc.size.width >> level)) + ASSERT(level >= mTextureState.baseLevel); + const size_t relativeLevel = level - mTextureState.baseLevel; + if (levelImageDesc.size.width != std::max(1, baseImageDesc.size.width >> relativeLevel)) { return false; } - if (levelImageDesc.size.height != std::max(1, baseImageDesc.size.height >> level)) + if (levelImageDesc.size.height != std::max(1, baseImageDesc.size.height >> relativeLevel)) { return false; } if (mTarget == GL_TEXTURE_3D) { - if (levelImageDesc.size.depth != std::max(1, baseImageDesc.size.depth >> level)) + if (levelImageDesc.size.depth != std::max(1, baseImageDesc.size.depth >> relativeLevel)) { return false; } @@ -839,16 +852,9 @@ Texture::SamplerCompletenessCache::SamplerCompletenessCache() { } -GLsizei Texture::getAttachmentWidth(const gl::FramebufferAttachment::Target &target) const +Extents Texture::getAttachmentSize(const gl::FramebufferAttachment::Target &target) const { - return static_cast( - getWidth(target.textureIndex().type, target.textureIndex().mipIndex)); -} - -GLsizei Texture::getAttachmentHeight(const gl::FramebufferAttachment::Target &target) const -{ - return static_cast( - getHeight(target.textureIndex().type, target.textureIndex().mipIndex)); + return getImageDesc(target.textureIndex().type, target.textureIndex().mipIndex).size; } GLenum Texture::getAttachmentInternalFormat(const gl::FramebufferAttachment::Target &target) const diff --git a/gfx/angle/src/libANGLE/Texture.h b/gfx/angle/src/libANGLE/Texture.h index bb1b804f12..7ca8a456fc 100644 --- a/gfx/angle/src/libANGLE/Texture.h +++ b/gfx/angle/src/libANGLE/Texture.h @@ -15,6 +15,7 @@ #include "angle_gl.h" #include "common/debug.h" #include "libANGLE/Caps.h" +#include "libANGLE/Debug.h" #include "libANGLE/Constants.h" #include "libANGLE/Error.h" #include "libANGLE/FramebufferAttachment.h" @@ -33,14 +34,19 @@ class Context; class Framebuffer; struct Data; -bool IsMipmapFiltered(const gl::SamplerState &samplerState); +bool IsMipmapFiltered(const SamplerState &samplerState); -class Texture final : public egl::ImageSibling, public gl::FramebufferAttachmentObject +class Texture final : public egl::ImageSibling, + public FramebufferAttachmentObject, + public LabeledObject { public: Texture(rx::TextureImpl *impl, GLuint id, GLenum target); ~Texture() override; + void setLabel(const std::string &label) override; + const std::string &getLabel() const override; + GLenum getTarget() const; void setSwizzleRed(GLenum swizzleRed); @@ -166,8 +172,7 @@ class Texture final : public egl::ImageSibling, public gl::FramebufferAttachment const rx::TextureImpl *getImplementation() const { return mTexture; } // FramebufferAttachmentObject implementation - GLsizei getAttachmentWidth(const FramebufferAttachment::Target &target) const override; - GLsizei getAttachmentHeight(const FramebufferAttachment::Target &target) const override; + Extents getAttachmentSize(const FramebufferAttachment::Target &target) const override; GLenum getAttachmentInternalFormat(const FramebufferAttachment::Target &target) const override; GLsizei getAttachmentSamples(const FramebufferAttachment::Target &target) const override; @@ -185,6 +190,8 @@ class Texture final : public egl::ImageSibling, public gl::FramebufferAttachment rx::TextureImpl *mTexture; + std::string mLabel; + TextureState mTextureState; GLenum mTarget; diff --git a/gfx/angle/src/libANGLE/TransformFeedback.cpp b/gfx/angle/src/libANGLE/TransformFeedback.cpp index 274872e3fb..b7961971d0 100644 --- a/gfx/angle/src/libANGLE/TransformFeedback.cpp +++ b/gfx/angle/src/libANGLE/TransformFeedback.cpp @@ -13,9 +13,10 @@ namespace gl { -TransformFeedback::TransformFeedback(rx::TransformFeedbackImpl* impl, GLuint id, const Caps &caps) +TransformFeedback::TransformFeedback(rx::TransformFeedbackImpl *impl, GLuint id, const Caps &caps) : RefCountObject(id), mImplementation(impl), + mLabel(), mActive(false), mPrimitiveMode(GL_NONE), mPaused(false), @@ -36,6 +37,16 @@ TransformFeedback::~TransformFeedback() SafeDelete(mImplementation); } +void TransformFeedback::setLabel(const std::string &label) +{ + mLabel = label; +} + +const std::string &TransformFeedback::getLabel() const +{ + return mLabel; +} + void TransformFeedback::begin(GLenum primitiveMode) { mActive = true; @@ -85,6 +96,24 @@ void TransformFeedback::bindGenericBuffer(Buffer *buffer) mImplementation->bindGenericBuffer(mGenericBuffer); } +void TransformFeedback::detachBuffer(GLuint bufferName) +{ + for (size_t index = 0; index < mIndexedBuffers.size(); index++) + { + if (mIndexedBuffers[index].id() == bufferName) + { + mIndexedBuffers[index].set(nullptr); + mImplementation->bindIndexedBuffer(index, mIndexedBuffers[index]); + } + } + + if (mGenericBuffer.id() == bufferName) + { + mGenericBuffer.set(nullptr); + mImplementation->bindGenericBuffer(mGenericBuffer); + } +} + const BindingPointer &TransformFeedback::getGenericBuffer() const { return mGenericBuffer; diff --git a/gfx/angle/src/libANGLE/TransformFeedback.h b/gfx/angle/src/libANGLE/TransformFeedback.h index 4345499f09..098e4ea4d6 100644 --- a/gfx/angle/src/libANGLE/TransformFeedback.h +++ b/gfx/angle/src/libANGLE/TransformFeedback.h @@ -10,6 +10,7 @@ #include "libANGLE/RefCountObject.h" #include "common/angleutils.h" +#include "libANGLE/Debug.h" #include "angle_gl.h" @@ -23,12 +24,15 @@ namespace gl class Buffer; struct Caps; -class TransformFeedback : public RefCountObject +class TransformFeedback final : public RefCountObject, public LabeledObject { public: TransformFeedback(rx::TransformFeedbackImpl* impl, GLuint id, const Caps &caps); virtual ~TransformFeedback(); + void setLabel(const std::string &label) override; + const std::string &getLabel() const override; + void begin(GLenum primitiveMode); void end(); void pause(); @@ -45,12 +49,16 @@ class TransformFeedback : public RefCountObject const OffsetBindingPointer &getIndexedBuffer(size_t index) const; size_t getIndexedBufferCount() const; + void detachBuffer(GLuint bufferName); + rx::TransformFeedbackImpl *getImplementation(); const rx::TransformFeedbackImpl *getImplementation() const; private: rx::TransformFeedbackImpl* mImplementation; + std::string mLabel; + bool mActive; GLenum mPrimitiveMode; bool mPaused; diff --git a/gfx/angle/src/libANGLE/VertexArray.cpp b/gfx/angle/src/libANGLE/VertexArray.cpp index afa433247c..8d51e9b469 100644 --- a/gfx/angle/src/libANGLE/VertexArray.cpp +++ b/gfx/angle/src/libANGLE/VertexArray.cpp @@ -15,8 +15,7 @@ namespace gl { VertexArray::Data::Data(size_t maxAttribs) - : mVertexAttributes(maxAttribs), - mMaxEnabledAttribute(0) + : mLabel(), mVertexAttributes(maxAttribs), mMaxEnabledAttribute(0) { } @@ -46,6 +45,16 @@ GLuint VertexArray::id() const return mId; } +void VertexArray::setLabel(const std::string &label) +{ + mData.mLabel = label; +} + +const std::string &VertexArray::getLabel() const +{ + return mData.mLabel; +} + void VertexArray::detachBuffer(GLuint bufferName) { for (size_t attribute = 0; attribute < getMaxAttribs(); attribute++) diff --git a/gfx/angle/src/libANGLE/VertexArray.h b/gfx/angle/src/libANGLE/VertexArray.h index 9ac7f7b757..6bc267d399 100644 --- a/gfx/angle/src/libANGLE/VertexArray.h +++ b/gfx/angle/src/libANGLE/VertexArray.h @@ -15,6 +15,7 @@ #include "libANGLE/RefCountObject.h" #include "libANGLE/Constants.h" +#include "libANGLE/Debug.h" #include "libANGLE/State.h" #include "libANGLE/VertexAttribute.h" @@ -30,7 +31,7 @@ namespace gl { class Buffer; -class VertexArray +class VertexArray final : public LabeledObject { public: VertexArray(rx::ImplFactory *factory, GLuint id, size_t maxAttribs); @@ -38,6 +39,9 @@ class VertexArray GLuint id() const; + void setLabel(const std::string &label) override; + const std::string &getLabel() const override; + const VertexAttribute &getVertexAttribute(size_t attributeIndex) const; void detachBuffer(GLuint bufferName); @@ -63,6 +67,8 @@ class VertexArray explicit Data(size_t maxAttribs); ~Data(); + const std::string &getLabel() const { return mLabel; } + const BindingPointer &getElementArrayBuffer() const { return mElementArrayBuffer; } size_t getMaxAttribs() const { return mVertexAttributes.size(); } size_t getMaxEnabledAttribute() const { return mMaxEnabledAttribute; } @@ -74,6 +80,7 @@ class VertexArray private: friend class VertexArray; + std::string mLabel; std::vector mVertexAttributes; BindingPointer mElementArrayBuffer; size_t mMaxEnabledAttribute; @@ -102,6 +109,7 @@ class VertexArray typedef std::bitset DirtyBits; void syncImplState(); + bool hasAnyDirtyBit() const { return mDirtyBits.any(); } private: GLuint mId; diff --git a/gfx/angle/src/libANGLE/angletypes.cpp b/gfx/angle/src/libANGLE/angletypes.cpp index 90b6af7999..fa5b157906 100644 --- a/gfx/angle/src/libANGLE/angletypes.cpp +++ b/gfx/angle/src/libANGLE/angletypes.cpp @@ -133,4 +133,13 @@ bool Box::operator!=(const Box &other) const return !(*this == other); } +bool operator==(const Extents &lhs, const Extents &rhs) +{ + return lhs.width == rhs.width && lhs.height == rhs.height && lhs.depth == rhs.depth; +} + +bool operator!=(const Extents &lhs, const Extents &rhs) +{ + return !(lhs == rhs); +} } diff --git a/gfx/angle/src/libANGLE/angletypes.h b/gfx/angle/src/libANGLE/angletypes.h index d64a458eb4..c29ad06bd2 100644 --- a/gfx/angle/src/libANGLE/angletypes.h +++ b/gfx/angle/src/libANGLE/angletypes.h @@ -68,13 +68,21 @@ typedef Color ColorUI; struct Rectangle { + Rectangle() : x(0), y(0), width(0), height(0) {} + Rectangle(int x_in, int y_in, int width_in, int height_in) + : x(x_in), y(y_in), width(width_in), height(height_in) + { + } + + int x0() const { return x; } + int y0() const { return y; } + int x1() const { return x + width; } + int y1() const { return y + height; } + int x; int y; int width; int height; - - Rectangle() : x(0), y(0), width(0), height(0) { } - Rectangle(int x_in, int y_in, int width_in, int height_in) : x(x_in), y(y_in), width(width_in), height(height_in) { } }; bool operator==(const Rectangle &a, const Rectangle &b); @@ -104,6 +112,9 @@ struct Extents bool empty() const { return (width * height * depth) == 0; } }; +bool operator==(const Extents &lhs, const Extents &rhs); +bool operator!=(const Extents &lhs, const Extents &rhs); + struct Box { int x; diff --git a/gfx/angle/src/libANGLE/formatutils.cpp b/gfx/angle/src/libANGLE/formatutils.cpp index 949141330d..3a4df126c5 100644 --- a/gfx/angle/src/libANGLE/formatutils.cpp +++ b/gfx/angle/src/libANGLE/formatutils.cpp @@ -219,6 +219,54 @@ static bool RequireExtOrExt(GLuint, const Extensions &extensions) return extensions.*bool1 || extensions.*bool2; } +// Special function for half float formats with three or four channels. +static bool HalfFloatSupport(GLuint clientVersion, const Extensions &extensions) +{ + return clientVersion >= 3 || extensions.textureHalfFloat; +} + +static bool HalfFloatRenderableSupport(GLuint clientVersion, const Extensions &extensions) +{ + return HalfFloatSupport(clientVersion, extensions) && extensions.colorBufferHalfFloat; +} + +// Special function for half float formats with one or two channels. +static bool HalfFloatSupportRG(GLuint clientVersion, const Extensions &extensions) +{ + return clientVersion >= 3 || (extensions.textureHalfFloat && extensions.textureRG); +} + +static bool HalfFloatRenderableSupportRG(GLuint clientVersion, const Extensions &extensions) +{ + return HalfFloatSupportRG(clientVersion, extensions) && extensions.colorBufferHalfFloat; +} + +// Special function for float formats with three or four channels. +static bool FloatSupport(GLuint clientVersion, const Extensions &extensions) +{ + return clientVersion >= 3 || extensions.textureFloat; +} + +static bool FloatRenderableSupport(GLuint clientVersion, const Extensions &extensions) +{ + // We don't expose colorBufferFloat in ES2, but we silently support rendering to float. + return FloatSupport(clientVersion, extensions) && + (extensions.colorBufferFloat || clientVersion == 2); +} + +// Special function for float formats with one or two channels. +static bool FloatSupportRG(GLuint clientVersion, const Extensions &extensions) +{ + return clientVersion >= 3 || (extensions.textureFloat && extensions.textureRG); +} + +static bool FloatRenderableSupportRG(GLuint clientVersion, const Extensions &extensions) +{ + // We don't expose colorBufferFloat in ES2, but we silently support rendering to float. + return FloatSupportRG(clientVersion, extensions) && + (extensions.colorBufferFloat || clientVersion == 2); +} + InternalFormat::InternalFormat() : redBits(0), greenBits(0), @@ -400,23 +448,23 @@ static InternalFormatInfoMap BuildInternalFormatInfoMap() map.insert(InternalFormatInfoPair(GL_BGR5_A1_ANGLEX, RGBAFormat( 5, 5, 5, 1, 0, GL_BGRA_EXT, GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT, GL_UNSIGNED_NORMALIZED, false, RequireExt<&Extensions::textureFormatBGRA8888>, RequireExt<&Extensions::textureFormatBGRA8888>, AlwaysSupported))); // Floating point renderability and filtering is provided by OES_texture_float and OES_texture_half_float - // | Internal format | | D |S | Format | Type | Comp | SRGB | Texture supported | Renderable | Filterable | - // | | | | | | | type | | | | | - map.insert(InternalFormatInfoPair(GL_R16F, RGBAFormat(16, 0, 0, 0, 0, GL_RED, GL_HALF_FLOAT, GL_FLOAT, false, RequireESOrExtAndExt<3, &Extensions::textureHalfFloat, &Extensions::textureRG>, RequireESOrExtAndExt<3, &Extensions::textureHalfFloat, &Extensions::textureRG>, RequireExt<&Extensions::textureHalfFloatLinear>))); - map.insert(InternalFormatInfoPair(GL_RG16F, RGBAFormat(16, 16, 0, 0, 0, GL_RG, GL_HALF_FLOAT, GL_FLOAT, false, RequireESOrExtAndExt<3, &Extensions::textureHalfFloat, &Extensions::textureRG>, RequireESOrExtAndExt<3, &Extensions::textureHalfFloat, &Extensions::textureRG>, RequireExt<&Extensions::textureHalfFloatLinear>))); - map.insert(InternalFormatInfoPair(GL_RGB16F, RGBAFormat(16, 16, 16, 0, 0, GL_RGB, GL_HALF_FLOAT, GL_FLOAT, false, RequireESOrExt<3, &Extensions::textureHalfFloat>, RequireESOrExt<3, &Extensions::textureHalfFloat>, RequireExt<&Extensions::textureHalfFloatLinear>))); - map.insert(InternalFormatInfoPair(GL_RGBA16F, RGBAFormat(16, 16, 16, 16, 0, GL_RGBA, GL_HALF_FLOAT, GL_FLOAT, false, RequireESOrExt<3, &Extensions::textureHalfFloat>, RequireESOrExt<3, &Extensions::textureHalfFloat>, RequireExt<&Extensions::textureHalfFloatLinear>))); - map.insert(InternalFormatInfoPair(GL_R32F, RGBAFormat(32, 0, 0, 0, 0, GL_RED, GL_FLOAT, GL_FLOAT, false, RequireESOrExtAndExt<3, &Extensions::textureFloat, &Extensions::textureRG>, RequireESOrExtAndExt<3, &Extensions::textureFloat, &Extensions::textureRG>, RequireExt<&Extensions::textureFloatLinear> ))); - map.insert(InternalFormatInfoPair(GL_RG32F, RGBAFormat(32, 32, 0, 0, 0, GL_RG, GL_FLOAT, GL_FLOAT, false, RequireESOrExtAndExt<3, &Extensions::textureFloat, &Extensions::textureRG>, RequireESOrExtAndExt<3, &Extensions::textureFloat, &Extensions::textureRG>, RequireExt<&Extensions::textureFloatLinear> ))); - map.insert(InternalFormatInfoPair(GL_RGB32F, RGBAFormat(32, 32, 32, 0, 0, GL_RGB, GL_FLOAT, GL_FLOAT, false, RequireESOrExt<3, &Extensions::textureFloat>, RequireESOrExt<3, &Extensions::textureFloat>, RequireExt<&Extensions::textureFloatLinear> ))); - map.insert(InternalFormatInfoPair(GL_RGBA32F, RGBAFormat(32, 32, 32, 32, 0, GL_RGBA, GL_FLOAT, GL_FLOAT, false, RequireESOrExt<3, &Extensions::textureFloat>, RequireESOrExt<3, &Extensions::textureFloat>, RequireExt<&Extensions::textureFloatLinear> ))); + // | Internal format | | D |S | Format | Type | Comp | SRGB | Texture supported | Renderable | Filterable | + // | | | | | | | type | | | | | + map.insert(InternalFormatInfoPair(GL_R16F, RGBAFormat(16, 0, 0, 0, 0, GL_RED, GL_HALF_FLOAT, GL_FLOAT, false, HalfFloatSupportRG, HalfFloatRenderableSupportRG, RequireExt<&Extensions::textureHalfFloatLinear>))); + map.insert(InternalFormatInfoPair(GL_RG16F, RGBAFormat(16, 16, 0, 0, 0, GL_RG, GL_HALF_FLOAT, GL_FLOAT, false, HalfFloatSupportRG, HalfFloatRenderableSupportRG, RequireExt<&Extensions::textureHalfFloatLinear>))); + map.insert(InternalFormatInfoPair(GL_RGB16F, RGBAFormat(16, 16, 16, 0, 0, GL_RGB, GL_HALF_FLOAT, GL_FLOAT, false, HalfFloatSupport, HalfFloatRenderableSupport, RequireExt<&Extensions::textureHalfFloatLinear>))); + map.insert(InternalFormatInfoPair(GL_RGBA16F, RGBAFormat(16, 16, 16, 16, 0, GL_RGBA, GL_HALF_FLOAT, GL_FLOAT, false, HalfFloatSupport, HalfFloatRenderableSupport, RequireExt<&Extensions::textureHalfFloatLinear>))); + map.insert(InternalFormatInfoPair(GL_R32F, RGBAFormat(32, 0, 0, 0, 0, GL_RED, GL_FLOAT, GL_FLOAT, false, FloatSupportRG, FloatRenderableSupportRG, RequireExt<&Extensions::textureFloatLinear> ))); + map.insert(InternalFormatInfoPair(GL_RG32F, RGBAFormat(32, 32, 0, 0, 0, GL_RG, GL_FLOAT, GL_FLOAT, false, FloatSupportRG, FloatRenderableSupportRG, RequireExt<&Extensions::textureFloatLinear> ))); + map.insert(InternalFormatInfoPair(GL_RGB32F, RGBAFormat(32, 32, 32, 0, 0, GL_RGB, GL_FLOAT, GL_FLOAT, false, FloatSupport, FloatRenderableSupport, RequireExt<&Extensions::textureFloatLinear> ))); + map.insert(InternalFormatInfoPair(GL_RGBA32F, RGBAFormat(32, 32, 32, 32, 0, GL_RGBA, GL_FLOAT, GL_FLOAT, false, FloatSupport, FloatRenderableSupport, RequireExt<&Extensions::textureFloatLinear> ))); // Depth stencil formats // | Internal format | | D |S | X | Format | Type | Component type | Supported | Renderable | Filterable | map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT16, DepthStencilFormat(16, 0, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, GL_UNSIGNED_NORMALIZED, RequireES<2>, RequireES<2>, RequireESOrExt<3, &Extensions::depthTextures>))); map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT24, DepthStencilFormat(24, 0, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, GL_UNSIGNED_NORMALIZED, RequireES<3>, RequireES<3>, RequireESOrExt<3, &Extensions::depthTextures>))); map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT32F, DepthStencilFormat(32, 0, 0, GL_DEPTH_COMPONENT, GL_FLOAT, GL_FLOAT, RequireES<3>, RequireES<3>, RequireESOrExt<3, &Extensions::depthTextures>))); - map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT32_OES, DepthStencilFormat(32, 0, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, GL_UNSIGNED_NORMALIZED, RequireExt<&Extensions::depthTextures>, RequireExt<&Extensions::depthTextures>, AlwaysSupported ))); + map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT32_OES, DepthStencilFormat(32, 0, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, GL_UNSIGNED_NORMALIZED, RequireExtOrExt<&Extensions::depthTextures, &Extensions::depth32>, RequireExtOrExt<&Extensions::depthTextures, &Extensions::depth32>, AlwaysSupported ))); map.insert(InternalFormatInfoPair(GL_DEPTH24_STENCIL8, DepthStencilFormat(24, 8, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, GL_UNSIGNED_NORMALIZED, RequireESOrExt<3, &Extensions::depthTextures>, RequireESOrExtOrExt<3, &Extensions::depthTextures, &Extensions::packedDepthStencil>, AlwaysSupported ))); map.insert(InternalFormatInfoPair(GL_DEPTH32F_STENCIL8, DepthStencilFormat(32, 8, 24, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, GL_FLOAT, RequireES<3>, RequireES<3>, AlwaysSupported ))); // STENCIL_INDEX8 is special-cased, see around the bottom of the list. @@ -517,6 +565,10 @@ static InternalFormatInfoMap BuildInternalFormatInfoMap() // - It affects only validation of internalformat in RenderbufferStorageMultisample. // | Internal format | |D |S |X | Format | Type | Component type | Supported | Renderable | Filterable | map.insert(InternalFormatInfoPair(GL_STENCIL_INDEX8, DepthStencilFormat(0, 8, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_BYTE, GL_UNSIGNED_NORMALIZED, RequireES<2>, RequireES<2>, NeverSupported))); + + // From GL_ANGLE_lossy_etc_decode + map.insert(InternalFormatInfoPair(GL_ETC1_RGB8_LOSSY_DECODE_ANGLE, CompressedFormat(4, 4, 64, 3, GL_ETC1_RGB8_OES, GL_UNSIGNED_BYTE, false, RequireExt<&Extensions::lossyETCDecode>, NeverSupported, AlwaysSupported))); + // clang-format on return map; diff --git a/gfx/angle/src/libANGLE/moz.build b/gfx/angle/src/libANGLE/moz.build index 16d22ece63..53e0c96c2c 100644 --- a/gfx/angle/src/libANGLE/moz.build +++ b/gfx/angle/src/libANGLE/moz.build @@ -113,6 +113,7 @@ UNIFIED_SOURCES += [ 'Config.cpp', 'Context.cpp', 'Data.cpp', + 'Debug.cpp', 'Device.cpp', 'Error.cpp', 'Fence.cpp', @@ -127,7 +128,6 @@ UNIFIED_SOURCES += [ 'Program.cpp', 'Query.cpp', 'queryconversions.cpp', - 'RefCountObject.cpp', 'Renderbuffer.cpp', 'renderer/d3d/BufferD3D.cpp', 'renderer/d3d/CompilerD3D.cpp', @@ -145,6 +145,7 @@ UNIFIED_SOURCES += [ 'renderer/d3d/d3d9/renderer9_utils.cpp', 'renderer/d3d/d3d9/RenderTarget9.cpp', 'renderer/d3d/d3d9/ShaderExecutable9.cpp', + 'renderer/d3d/d3d9/StateManager9.cpp', 'renderer/d3d/d3d9/SwapChain9.cpp', 'renderer/d3d/d3d9/TextureStorage9.cpp', 'renderer/d3d/d3d9/VertexBuffer9.cpp', @@ -196,6 +197,7 @@ UNIFIED_SOURCES += [ 'renderer/gl/TransformFeedbackGL.cpp', 'renderer/gl/VertexArrayGL.cpp', 'renderer/gl/wgl/DisplayWGL.cpp', + 'renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.cpp', 'renderer/gl/wgl/FunctionsWGL.cpp', 'renderer/gl/wgl/PbufferSurfaceWGL.cpp', 'renderer/gl/wgl/wgl_utils.cpp', @@ -253,11 +255,11 @@ if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']: 'renderer/d3d/d3d11/TextureStorage11.cpp', 'renderer/d3d/d3d11/Trim11.cpp', 'renderer/d3d/d3d11/VertexBuffer11.cpp', - 'renderer/d3d/d3d11/win32/NativeWindow.cpp', ] if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']: SOURCES += [ 'renderer/d3d/d3d11/SwapChain11.cpp', + 'renderer/d3d/d3d11/win32/NativeWindow.cpp', ] @@ -308,6 +310,8 @@ LOCAL_INCLUDES += [ '../../include', '../../src', '../../src/third_party/khronos DEFINES['LIBANGLE_IMPLEMENTATION'] = "1" DEFINES['ANGLE_ENABLE_HLSL'] = "1" +DEFINES['ANGLE_ENABLE_GLSL'] = "1" +DEFINES['ANGLE_ENABLE_ESSL'] = "1" DEFINES['ANGLE_ENABLE_KEYEDMUTEX'] = "1" DEFINES['ANGLE_DEFAULT_D3D11'] = "0" diff --git a/gfx/angle/src/libANGLE/renderer/DeviceImpl.h b/gfx/angle/src/libANGLE/renderer/DeviceImpl.h index 99793f5686..550bc1e2d9 100644 --- a/gfx/angle/src/libANGLE/renderer/DeviceImpl.h +++ b/gfx/angle/src/libANGLE/renderer/DeviceImpl.h @@ -29,6 +29,7 @@ class DeviceImpl : angle::NonCopyable virtual egl::Error getDevice(void **outValue) = 0; virtual EGLint getType() = 0; virtual void generateExtensions(egl::DeviceExtensions *outExtensions) const = 0; + virtual bool deviceExternallySourced() = 0; }; } diff --git a/gfx/angle/src/libANGLE/renderer/DisplayImpl.cpp b/gfx/angle/src/libANGLE/renderer/DisplayImpl.cpp index 2eb44eb5ba..8061189f0a 100644 --- a/gfx/angle/src/libANGLE/renderer/DisplayImpl.cpp +++ b/gfx/angle/src/libANGLE/renderer/DisplayImpl.cpp @@ -21,10 +21,7 @@ DisplayImpl::DisplayImpl() DisplayImpl::~DisplayImpl() { - while (!mSurfaceSet.empty()) - { - destroySurface(*mSurfaceSet.begin()); - } + ASSERT(mSurfaceSet.empty()); } void DisplayImpl::destroySurface(egl::Surface *surface) diff --git a/gfx/angle/src/libANGLE/renderer/DisplayImpl.h b/gfx/angle/src/libANGLE/renderer/DisplayImpl.h index 59561f6004..9e38f63370 100644 --- a/gfx/angle/src/libANGLE/renderer/DisplayImpl.h +++ b/gfx/angle/src/libANGLE/renderer/DisplayImpl.h @@ -64,8 +64,9 @@ class DisplayImpl : angle::NonCopyable egl::ImageSibling *buffer, const egl::AttributeMap &attribs) = 0; - virtual egl::Error createContext(const egl::Config *config, const gl::Context *shareContext, const egl::AttributeMap &attribs, - gl::Context **outContext) = 0; + virtual gl::Context *createContext(const egl::Config *config, + const gl::Context *shareContext, + const egl::AttributeMap &attribs) = 0; virtual egl::Error makeCurrent(egl::Surface *drawSurface, egl::Surface *readSurface, gl::Context *context) = 0; @@ -81,6 +82,11 @@ class DisplayImpl : angle::NonCopyable virtual egl::Error getDevice(DeviceImpl **device) = 0; + virtual egl::Error waitClient() const = 0; + virtual egl::Error waitNative(EGLint engine, + egl::Surface *drawSurface, + egl::Surface *readSurface) const = 0; + const egl::Caps &getCaps() const; typedef std::set SurfaceSet; diff --git a/gfx/angle/src/libANGLE/renderer/FramebufferImpl.h b/gfx/angle/src/libANGLE/renderer/FramebufferImpl.h index dddd9b8778..680122d0ed 100644 --- a/gfx/angle/src/libANGLE/renderer/FramebufferImpl.h +++ b/gfx/angle/src/libANGLE/renderer/FramebufferImpl.h @@ -31,23 +31,28 @@ class FramebufferImpl : angle::NonCopyable explicit FramebufferImpl(const gl::Framebuffer::Data &data) : mData(data) { } virtual ~FramebufferImpl() { } - virtual void onUpdateColorAttachment(size_t index) = 0; - virtual void onUpdateDepthAttachment() = 0; - virtual void onUpdateStencilAttachment() = 0; - virtual void onUpdateDepthStencilAttachment() = 0; - - virtual void setDrawBuffers(size_t count, const GLenum *buffers) = 0; - virtual void setReadBuffer(GLenum buffer) = 0; - virtual gl::Error discard(size_t count, const GLenum *attachments) = 0; virtual gl::Error invalidate(size_t count, const GLenum *attachments) = 0; virtual gl::Error invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area) = 0; virtual gl::Error clear(const gl::Data &data, GLbitfield mask) = 0; - virtual gl::Error clearBufferfv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLfloat *values) = 0; - virtual gl::Error clearBufferuiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLuint *values) = 0; - virtual gl::Error clearBufferiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLint *values) = 0; - virtual gl::Error clearBufferfi(const gl::State &state, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) = 0; + virtual gl::Error clearBufferfv(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + const GLfloat *values) = 0; + virtual gl::Error clearBufferuiv(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + const GLuint *values) = 0; + virtual gl::Error clearBufferiv(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + const GLint *values) = 0; + virtual gl::Error clearBufferfi(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + GLfloat depth, + GLint stencil) = 0; virtual GLenum getImplementationColorReadFormat() const = 0; virtual GLenum getImplementationColorReadType() const = 0; @@ -56,7 +61,9 @@ class FramebufferImpl : angle::NonCopyable virtual gl::Error blit(const gl::State &state, const gl::Rectangle &sourceArea, const gl::Rectangle &destArea, GLbitfield mask, GLenum filter, const gl::Framebuffer *sourceFramebuffer) = 0; - virtual GLenum checkStatus() const = 0; + virtual bool checkStatus() const = 0; + + virtual void syncState(const gl::Framebuffer::DirtyBits &dirtyBits) = 0; const gl::Framebuffer::Data &getData() const { return mData; } diff --git a/gfx/angle/src/libANGLE/renderer/FramebufferImpl_mock.h b/gfx/angle/src/libANGLE/renderer/FramebufferImpl_mock.h index d5a0dc677e..57c95342d7 100644 --- a/gfx/angle/src/libANGLE/renderer/FramebufferImpl_mock.h +++ b/gfx/angle/src/libANGLE/renderer/FramebufferImpl_mock.h @@ -23,23 +23,15 @@ class MockFramebufferImpl : public rx::FramebufferImpl MockFramebufferImpl() : rx::FramebufferImpl(gl::Framebuffer::Data()) {} virtual ~MockFramebufferImpl() { destroy(); } - MOCK_METHOD1(onUpdateColorAttachment, void(size_t)); - MOCK_METHOD0(onUpdateDepthAttachment, void()); - MOCK_METHOD0(onUpdateStencilAttachment, void()); - MOCK_METHOD0(onUpdateDepthStencilAttachment, void()); - - MOCK_METHOD2(setDrawBuffers, void(size_t, const GLenum *)); - MOCK_METHOD1(setReadBuffer, void(GLenum)); - MOCK_METHOD2(discard, gl::Error(size_t, const GLenum *)); MOCK_METHOD2(invalidate, gl::Error(size_t, const GLenum *)); MOCK_METHOD3(invalidateSub, gl::Error(size_t, const GLenum *, const gl::Rectangle &)); MOCK_METHOD2(clear, gl::Error(const gl::Data &, GLbitfield)); - MOCK_METHOD4(clearBufferfv, gl::Error(const gl::State &, GLenum, GLint, const GLfloat *)); - MOCK_METHOD4(clearBufferuiv, gl::Error(const gl::State &, GLenum, GLint, const GLuint *)); - MOCK_METHOD4(clearBufferiv, gl::Error(const gl::State &, GLenum, GLint, const GLint *)); - MOCK_METHOD5(clearBufferfi, gl::Error(const gl::State &, GLenum, GLint, GLfloat, GLint)); + MOCK_METHOD4(clearBufferfv, gl::Error(const gl::Data &, GLenum, GLint, const GLfloat *)); + MOCK_METHOD4(clearBufferuiv, gl::Error(const gl::Data &, GLenum, GLint, const GLuint *)); + MOCK_METHOD4(clearBufferiv, gl::Error(const gl::Data &, GLenum, GLint, const GLint *)); + MOCK_METHOD5(clearBufferfi, gl::Error(const gl::Data &, GLenum, GLint, GLfloat, GLint)); MOCK_CONST_METHOD0(getImplementationColorReadFormat, GLenum()); MOCK_CONST_METHOD0(getImplementationColorReadType, GLenum()); @@ -55,11 +47,26 @@ class MockFramebufferImpl : public rx::FramebufferImpl GLenum, const gl::Framebuffer *)); - MOCK_CONST_METHOD0(checkStatus, GLenum()); + MOCK_CONST_METHOD0(checkStatus, bool()); + + MOCK_METHOD1(syncState, void(const gl::Framebuffer::DirtyBits &)); MOCK_METHOD0(destroy, void()); }; +inline ::testing::NiceMock *MakeFramebufferMock() +{ + ::testing::NiceMock *framebufferImpl = + new ::testing::NiceMock(); + // TODO(jmadill): add ON_CALLS for other returning methods + ON_CALL(*framebufferImpl, checkStatus()).WillByDefault(::testing::Return(true)); + + // We must mock the destructor since NiceMock doesn't work for destructors. + EXPECT_CALL(*framebufferImpl, destroy()).Times(1).RetiresOnSaturation(); + + return framebufferImpl; +} + } // namespace rx #endif // LIBANGLE_RENDERER_FRAMEBUFFERIMPLMOCK_H_ diff --git a/gfx/angle/src/libANGLE/renderer/ProgramImpl.h b/gfx/angle/src/libANGLE/renderer/ProgramImpl.h index a9d54b8e5d..1e688045a1 100644 --- a/gfx/angle/src/libANGLE/renderer/ProgramImpl.h +++ b/gfx/angle/src/libANGLE/renderer/ProgramImpl.h @@ -37,6 +37,7 @@ class ProgramImpl : angle::NonCopyable virtual LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) = 0; virtual gl::Error save(gl::BinaryOutputStream *stream) = 0; + virtual void setBinaryRetrievableHint(bool retrievable) = 0; virtual LinkResult link(const gl::Data &data, gl::InfoLog &infoLog) = 0; virtual GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) = 0; diff --git a/gfx/angle/src/libANGLE/renderer/ProgramImpl_mock.h b/gfx/angle/src/libANGLE/renderer/ProgramImpl_mock.h index dbc15345f0..d6aa238f64 100644 --- a/gfx/angle/src/libANGLE/renderer/ProgramImpl_mock.h +++ b/gfx/angle/src/libANGLE/renderer/ProgramImpl_mock.h @@ -25,6 +25,7 @@ class MockProgramImpl : public rx::ProgramImpl MOCK_METHOD2(load, LinkResult(gl::InfoLog &, gl::BinaryInputStream *)); MOCK_METHOD1(save, gl::Error(gl::BinaryOutputStream *)); + MOCK_METHOD1(setBinaryRetrievableHint, void(bool)); MOCK_METHOD2(link, LinkResult(const gl::Data &, gl::InfoLog &)); MOCK_METHOD2(validate, GLboolean(const gl::Caps &, gl::InfoLog *)); @@ -59,6 +60,16 @@ class MockProgramImpl : public rx::ProgramImpl MOCK_METHOD0(destroy, void()); }; +inline ::testing::NiceMock *MakeProgramMock() +{ + ::testing::NiceMock *programImpl = new ::testing::NiceMock(); + // TODO(jmadill): add ON_CALLS for returning methods + // We must mock the destructor since NiceMock doesn't work for destructors. + EXPECT_CALL(*programImpl, destroy()).Times(1).RetiresOnSaturation(); + + return programImpl; +} + } // namespace rx #endif // LIBANGLE_RENDERER_PROGRAMIMPLMOCK_H_ diff --git a/gfx/angle/src/libANGLE/renderer/QueryImpl.h b/gfx/angle/src/libANGLE/renderer/QueryImpl.h index bed63ea1b0..d738eb4ffc 100644 --- a/gfx/angle/src/libANGLE/renderer/QueryImpl.h +++ b/gfx/angle/src/libANGLE/renderer/QueryImpl.h @@ -26,8 +26,12 @@ class QueryImpl : angle::NonCopyable virtual gl::Error begin() = 0; virtual gl::Error end() = 0; + virtual gl::Error queryCounter() = 0; + virtual gl::Error getResult(GLint *params) = 0; virtual gl::Error getResult(GLuint *params) = 0; - virtual gl::Error isResultAvailable(GLuint *available) = 0; + virtual gl::Error getResult(GLint64 *params) = 0; + virtual gl::Error getResult(GLuint64 *params) = 0; + virtual gl::Error isResultAvailable(bool *available) = 0; GLenum getType() const { return mType; } diff --git a/gfx/angle/src/libANGLE/renderer/Renderer.h b/gfx/angle/src/libANGLE/renderer/Renderer.h index 06fe560d5c..d0da2b140c 100644 --- a/gfx/angle/src/libANGLE/renderer/Renderer.h +++ b/gfx/angle/src/libANGLE/renderer/Renderer.h @@ -82,7 +82,6 @@ class Renderer : public ImplFactory virtual bool testDeviceLost() = 0; virtual bool testDeviceResettable() = 0; - virtual VendorID getVendorId() const = 0; virtual std::string getVendorString() const = 0; virtual std::string getRendererDescription() const = 0; @@ -92,6 +91,13 @@ class Renderer : public ImplFactory virtual void syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits) = 0; + // Disjoint timer queries + virtual GLint getGPUDisjoint() = 0; + virtual GLint64 getTimestamp() = 0; + + // Context switching + virtual void onMakeCurrent(const gl::Data &data) = 0; + // Renderer capabilities const gl::Caps &getRendererCaps() const; const gl::TextureCapsMap &getRendererTextureCaps() const; diff --git a/gfx/angle/src/libANGLE/renderer/SurfaceImpl.h b/gfx/angle/src/libANGLE/renderer/SurfaceImpl.h index 1d5211bb9a..32125d542c 100644 --- a/gfx/angle/src/libANGLE/renderer/SurfaceImpl.h +++ b/gfx/angle/src/libANGLE/renderer/SurfaceImpl.h @@ -36,7 +36,7 @@ class SurfaceImpl : public FramebufferAttachmentObjectImpl virtual egl::Error swap() = 0; virtual egl::Error postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) = 0; virtual egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) = 0; - virtual egl::Error bindTexImage(EGLint buffer) = 0; + virtual egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) = 0; virtual egl::Error releaseTexImage(EGLint buffer) = 0; virtual void setSwapInterval(EGLint interval) = 0; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.cpp index 91dddc505c..ffca99c3ac 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.cpp @@ -37,10 +37,15 @@ BufferD3D::~BufferD3D() SafeDelete(mStaticVertexBuffer); SafeDelete(mStaticIndexBuffer); - // Empty the cache of static vertex buffers too + emptyStaticBufferCache(); +} + +void BufferD3D::emptyStaticBufferCache() +{ if (mStaticBufferCache != nullptr) { SafeDeleteContainer(*mStaticBufferCache); + SafeDelete(mStaticBufferCache); } mStaticBufferCacheTotalSize = 0; @@ -202,13 +207,7 @@ void BufferD3D::invalidateStaticData(D3DBufferInvalidationType invalidationType) { if (invalidationType == D3D_BUFFER_INVALIDATE_WHOLE_CACHE && mStaticBufferCache != nullptr) { - // Empty the cache of static vertex buffers too - for (StaticVertexBufferInterface *staticBuffer : *mStaticBufferCache) - { - SafeDelete(staticBuffer); - } - mStaticBufferCache->clear(); - mStaticBufferCacheTotalSize = 0; + emptyStaticBufferCache(); } if ((mStaticVertexBuffer && mStaticVertexBuffer->getBufferSize() != 0) || (mStaticIndexBuffer && mStaticIndexBuffer->getBufferSize() != 0)) diff --git a/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.h index 849064ced1..a27ca9857a 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.h @@ -71,6 +71,7 @@ class BufferD3D : public BufferImpl protected: void updateSerial(); void updateD3DBufferUsage(GLenum usage); + void emptyStaticBufferCache(); BufferFactoryD3D *mFactory; unsigned int mSerial; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/CompilerD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/CompilerD3D.cpp index a1ac0ef4fa..6f8d1717cd 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/CompilerD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/CompilerD3D.cpp @@ -3,7 +3,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -// CompilerGL: +// CompilerD3D: // Implementation of the D3D compiler methods. // @@ -12,26 +12,8 @@ namespace rx { -namespace -{ - -ShShaderOutput GetShaderOutputType(RendererClass rendererClass) -{ - if (rendererClass == RENDERER_D3D11) - { - return SH_HLSL11_OUTPUT; - } - else - { - ASSERT(rendererClass == RENDERER_D3D9); - return SH_HLSL9_OUTPUT; - } -} - -} // anonymous namespace - -CompilerD3D::CompilerD3D(RendererClass rendererClass) - : mTranslatorOutputType(GetShaderOutputType(rendererClass)) +CompilerD3D::CompilerD3D(ShShaderOutput translatorOutputType) + : mTranslatorOutputType(translatorOutputType) { } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/CompilerD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/CompilerD3D.h index f97de79c13..8f4334963d 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/CompilerD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/CompilerD3D.h @@ -18,7 +18,7 @@ namespace rx class CompilerD3D : public CompilerImpl { public: - CompilerD3D(RendererClass rendererClass); + CompilerD3D(ShShaderOutput translatorOutputType); ~CompilerD3D() override {} gl::Error release() override { return gl::Error(GL_NO_ERROR); } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.cpp index 1608228bc7..f40e6e6cab 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.cpp @@ -17,16 +17,78 @@ namespace rx { -DeviceD3D::DeviceD3D(void *device, EGLint deviceType) : mDevice(device), mDeviceType(deviceType) +DeviceD3D::DeviceD3D() + : mDevice(0), mDeviceType(0), mDeviceExternallySourced(false), mIsInitialized(false) { } +DeviceD3D::~DeviceD3D() +{ +#if defined(ANGLE_ENABLE_D3D11) + if (mDeviceType == EGL_D3D11_DEVICE_ANGLE) + { + // DeviceD3D holds a ref to an externally-sourced D3D11 device. We must release it. + ID3D11Device *device = reinterpret_cast(mDevice); + device->Release(); + } +#endif +} + egl::Error DeviceD3D::getDevice(void **outValue) { + if (!mIsInitialized) + { + *outValue = nullptr; + return egl::Error(EGL_BAD_DEVICE_EXT); + } + *outValue = mDevice; return egl::Error(EGL_SUCCESS); } +egl::Error DeviceD3D::initialize(void *device, + EGLint deviceType, + EGLBoolean deviceExternallySourced) +{ + ASSERT(!mIsInitialized); + if (mIsInitialized) + { + return egl::Error(EGL_BAD_DEVICE_EXT); + } + + mDevice = device; + mDeviceType = deviceType; + mDeviceExternallySourced = !!deviceExternallySourced; + +#if defined(ANGLE_ENABLE_D3D11) + if (mDeviceType == EGL_D3D11_DEVICE_ANGLE) + { + // Validate the device + IUnknown *iunknown = reinterpret_cast(device); + + ID3D11Device *d3dDevice = nullptr; + HRESULT hr = + iunknown->QueryInterface(__uuidof(ID3D11Device), reinterpret_cast(&d3dDevice)); + if (FAILED(hr)) + { + return egl::Error(EGL_BAD_ATTRIBUTE, "Invalid D3D device passed into EGLDeviceEXT"); + } + + // The QI to ID3D11Device adds a ref to the D3D11 device. + // Deliberately don't release the ref here, so that the DeviceD3D holds a ref to the + // D3D11 device. + } + else +#endif + { + ASSERT(!mDeviceExternallySourced); + } + + mIsInitialized = true; + + return egl::Error(EGL_SUCCESS); +} + EGLint DeviceD3D::getType() { return mDeviceType; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.h index 20276a7aca..1dd9979708 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.h @@ -18,15 +18,20 @@ namespace rx class DeviceD3D : public DeviceImpl { public: - DeviceD3D(void *device, EGLint deviceType); + DeviceD3D(); + ~DeviceD3D() override; + egl::Error initialize(void *device, EGLint deviceType, EGLBoolean external); egl::Error getDevice(void **outValue) override; EGLint getType() override; void generateExtensions(egl::DeviceExtensions *outExtensions) const override; + bool deviceExternallySourced() override { return mDeviceExternallySourced; } private: void *mDevice; EGLint mDeviceType; + bool mDeviceExternallySourced; + bool mIsInitialized; }; } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.cpp index 9daeb7a828..d4dc702582 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.cpp @@ -55,10 +55,13 @@ egl::Error CreateRendererD3D(egl::Display *display, RendererD3D **outRenderer) std::vector rendererCreationFunctions; - const auto &attribMap = display->getAttributeMap(); - EGLNativeDisplayType nativeDisplay = display->getNativeDisplayId(); + if (display->getPlatform() == EGL_PLATFORM_ANGLE_ANGLE) + { + const auto &attribMap = display->getAttributeMap(); + EGLNativeDisplayType nativeDisplay = display->getNativeDisplayId(); - EGLint requestedDisplayType = attribMap.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE); + EGLint requestedDisplayType = + attribMap.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE); # if defined(ANGLE_ENABLE_D3D11) if (nativeDisplay == EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE || @@ -77,27 +80,41 @@ egl::Error CreateRendererD3D(egl::Display *display, RendererD3D **outRenderer) } # endif - if (nativeDisplay != EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE && - nativeDisplay != EGL_D3D11_ONLY_DISPLAY_ANGLE && - requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE) - { + if (nativeDisplay != EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE && + nativeDisplay != EGL_D3D11_ONLY_DISPLAY_ANGLE && + requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE) + { // The default display is requested, try the D3D9 and D3D11 renderers, order them using // the definition of ANGLE_DEFAULT_D3D11 # if ANGLE_DEFAULT_D3D11 # if defined(ANGLE_ENABLE_D3D11) - rendererCreationFunctions.push_back(CreateTypedRendererD3D); + rendererCreationFunctions.push_back(CreateTypedRendererD3D); # endif # if defined(ANGLE_ENABLE_D3D9) - rendererCreationFunctions.push_back(CreateTypedRendererD3D); + rendererCreationFunctions.push_back(CreateTypedRendererD3D); # endif # else # if defined(ANGLE_ENABLE_D3D9) - rendererCreationFunctions.push_back(CreateTypedRendererD3D); + rendererCreationFunctions.push_back(CreateTypedRendererD3D); # endif # if defined(ANGLE_ENABLE_D3D11) - rendererCreationFunctions.push_back(CreateTypedRendererD3D); + rendererCreationFunctions.push_back(CreateTypedRendererD3D); # endif # endif + } + } + else if (display->getPlatform() == EGL_PLATFORM_DEVICE_EXT) + { +#if defined(ANGLE_ENABLE_D3D11) + if (display->getDevice()->getType() == EGL_D3D11_DEVICE_ANGLE) + { + rendererCreationFunctions.push_back(CreateTypedRendererD3D); + } +#endif + } + else + { + UNIMPLEMENTED(); } egl::Error result(EGL_NOT_INITIALIZED, "No available renderers."); @@ -155,6 +172,8 @@ SurfaceImpl *DisplayD3D::createWindowSurface(const egl::Config *configuration, EGLint width = attribs.get(EGL_WIDTH, 0); EGLint height = attribs.get(EGL_HEIGHT, 0); EGLint fixedSize = attribs.get(EGL_FIXED_SIZE_ANGLE, EGL_FALSE); + EGLint orientation = attribs.get(EGL_SURFACE_ORIENTATION_ANGLE, 0); + EGLint directComposition = attribs.get(EGL_DIRECT_COMPOSITION_ANGLE, EGL_FALSE); if (!fixedSize) { @@ -162,8 +181,8 @@ SurfaceImpl *DisplayD3D::createWindowSurface(const egl::Config *configuration, height = -1; } - return SurfaceD3D::createFromWindow( - mRenderer, mDisplay, configuration, window, fixedSize, width, height); + return SurfaceD3D::createFromWindow(mRenderer, mDisplay, configuration, window, fixedSize, + directComposition, width, height, orientation); } SurfaceImpl *DisplayD3D::createPbufferSurface(const egl::Config *configuration, @@ -210,17 +229,12 @@ egl::Error DisplayD3D::getDevice(DeviceImpl **device) return mRenderer->getEGLDevice(device); } -egl::Error DisplayD3D::createContext(const egl::Config *config, const gl::Context *shareContext, const egl::AttributeMap &attribs, - gl::Context **outContext) +gl::Context *DisplayD3D::createContext(const egl::Config *config, + const gl::Context *shareContext, + const egl::AttributeMap &attribs) { ASSERT(mRenderer != nullptr); - - EGLint clientVersion = attribs.get(EGL_CONTEXT_CLIENT_VERSION, 1); - bool notifyResets = (attribs.get(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT, EGL_NO_RESET_NOTIFICATION_EXT) == EGL_LOSE_CONTEXT_ON_RESET_EXT); - bool robustAccess = (attribs.get(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT, EGL_FALSE) == EGL_TRUE); - - *outContext = new gl::Context(config, clientVersion, shareContext, mRenderer, notifyResets, robustAccess); - return egl::Error(EGL_SUCCESS); + return new gl::Context(config, shareContext, mRenderer, attribs); } egl::Error DisplayD3D::makeCurrent(egl::Surface *drawSurface, egl::Surface *readSurface, gl::Context *context) @@ -326,4 +340,17 @@ void DisplayD3D::generateCaps(egl::Caps *outCaps) const outCaps->textureNPOT = mRenderer->getRendererExtensions().textureNPOT; } +egl::Error DisplayD3D::waitClient() const +{ + // Unimplemented as it is a noop on D3D + return egl::Error(EGL_SUCCESS); +} + +egl::Error DisplayD3D::waitNative(EGLint engine, + egl::Surface *drawSurface, + egl::Surface *readSurface) const +{ + // Unimplemented as it is a noop on D3D + return egl::Error(EGL_SUCCESS); +} } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.h index 7ce78daea5..0ce196dea2 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.h @@ -41,8 +41,9 @@ class DisplayD3D : public DisplayImpl egl::ImageSibling *buffer, const egl::AttributeMap &attribs) override; - egl::Error createContext(const egl::Config *config, const gl::Context *shareContext, const egl::AttributeMap &attribs, - gl::Context **outContext) override; + gl::Context *createContext(const egl::Config *config, + const gl::Context *shareContext, + const egl::AttributeMap &attribs) override; egl::Error makeCurrent(egl::Surface *drawSurface, egl::Surface *readSurface, gl::Context *context) override; @@ -58,6 +59,11 @@ class DisplayD3D : public DisplayImpl std::string getVendorString() const override; + egl::Error waitClient() const override; + egl::Error waitNative(EGLint engine, + egl::Surface *drawSurface, + egl::Surface *readSurface) const override; + private: void generateExtensions(egl::DisplayExtensions *outExtensions) const override; void generateCaps(egl::Caps *outCaps) const override; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.cpp b/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.cpp index 1d07b95c33..42a534f573 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.cpp @@ -412,6 +412,9 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::Data &data, const ShaderD3D *fragmentShader = GetImplAs(fragmentShaderGL); const int shaderModel = mRenderer->getMajorShaderModel(); + // usesViewScale() isn't supported in the D3D9 renderer + ASSERT(shaderModel >= 4 || !programMetadata.usesViewScale()); + bool useInstancedPointSpriteEmulation = programMetadata.usesPointSize() && mRenderer->getWorkarounds().useInstancedPointSpriteEmulation; @@ -464,18 +467,43 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::Data &data, // On D3D9 or D3D11 Feature Level 9, we need to emulate large viewports using dx_ViewAdjust. if (shaderModel >= 4 && mRenderer->getShaderModelSuffix() == "") { - vertexStream << " output.dx_Position.x = gl_Position.x;\n" - << " output.dx_Position.y = -gl_Position.y;\n" - << " output.dx_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n" + vertexStream << " output.dx_Position.x = gl_Position.x;\n"; + + if (programMetadata.usesViewScale()) + { + // This code assumes that dx_ViewScale.y = -1.0f when rendering to texture, and +1.0f + // when rendering to the default framebuffer. No other values are valid. + vertexStream << " output.dx_Position.y = dx_ViewScale.y * gl_Position.y;\n"; + } + else + { + vertexStream << " output.dx_Position.y = - gl_Position.y;\n"; + } + + vertexStream << " output.dx_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n" << " output.dx_Position.w = gl_Position.w;\n"; } else { vertexStream << " output.dx_Position.x = gl_Position.x * dx_ViewAdjust.z + " - "dx_ViewAdjust.x * gl_Position.w;\n" - << " output.dx_Position.y = -(gl_Position.y * dx_ViewAdjust.w + " - "dx_ViewAdjust.y * gl_Position.w);\n" - << " output.dx_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n" + "dx_ViewAdjust.x * gl_Position.w;\n"; + + // If usesViewScale() is true and we're using the D3D11 renderer via Feature Level 9_*, + // then we need to multiply the gl_Position.y by the viewScale. + // usesViewScale() isn't supported when using the D3D9 renderer. + if (programMetadata.usesViewScale() && + (shaderModel >= 4 && mRenderer->getShaderModelSuffix() != "")) + { + vertexStream << " output.dx_Position.y = dx_ViewScale.y * (gl_Position.y * " + "dx_ViewAdjust.w + dx_ViewAdjust.y * gl_Position.w);\n"; + } + else + { + vertexStream << " output.dx_Position.y = -(gl_Position.y * dx_ViewAdjust.w + " + "dx_ViewAdjust.y * gl_Position.w);\n"; + } + + vertexStream << " output.dx_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n" << " output.dx_Position.w = gl_Position.w;\n"; } @@ -523,11 +551,26 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::Data &data, if (useInstancedPointSpriteEmulation) { vertexStream << "\n" - << " gl_PointSize = clamp(gl_PointSize, minPointSize, maxPointSize);\n" - << " output.dx_Position.xyz += float3(input.spriteVertexPos.x * " - "gl_PointSize / (dx_ViewCoords.x*2), input.spriteVertexPos.y * " - "gl_PointSize / (dx_ViewCoords.y*2), input.spriteVertexPos.z) * " - "output.dx_Position.w;\n"; + << " gl_PointSize = clamp(gl_PointSize, minPointSize, maxPointSize);\n"; + + vertexStream << " output.dx_Position.x += (input.spriteVertexPos.x * gl_PointSize / " + "(dx_ViewCoords.x*2)) * output.dx_Position.w;"; + + if (programMetadata.usesViewScale()) + { + // Multiply by ViewScale to invert the rendering when appropriate + vertexStream << " output.dx_Position.y += (-dx_ViewScale.y * " + "input.spriteVertexPos.y * gl_PointSize / (dx_ViewCoords.y*2)) * " + "output.dx_Position.w;"; + } + else + { + vertexStream << " output.dx_Position.y += (input.spriteVertexPos.y * gl_PointSize / " + "(dx_ViewCoords.y*2)) * output.dx_Position.w;"; + } + + vertexStream + << " output.dx_Position.z += input.spriteVertexPos.z * output.dx_Position.w;\n"; if (programMetadata.usesPointCoord()) { @@ -606,6 +649,49 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::Data &data, "dx_ViewCoords.w;\n"; } + if (programMetadata.usesViewScale()) + { + // For Feature Level 9_3 and below, we need to correct gl_FragCoord.y to account + // for dx_ViewScale. On Feature Level 10_0+, gl_FragCoord.y is calculated above using + // dx_ViewCoords and is always correct irrespective of dx_ViewScale's value. + // NOTE: usesViewScale() can only be true on D3D11 (i.e. Shader Model 4.0+). + if (shaderModel >= 4 && mRenderer->getShaderModelSuffix() == "") + { + // Some assumptions: + // - dx_ViewScale.y = -1.0f when rendering to texture + // - dx_ViewScale.y = +1.0f when rendering to the default framebuffer + // - gl_FragCoord.y has been set correctly above. + // + // When rendering to the backbuffer, the code inverts gl_FragCoord's y coordinate. + // This involves subtracting the y coordinate from the height of the area being + // rendered to. + // + // First we calculate the height of the area being rendered to: + // render_area_height = (2.0f / (1.0f - input.gl_FragCoord.y * rhw)) * + // gl_FragCoord.y + // + // Note that when we're rendering to default FB, we want our output to be + // equivalent to: + // "gl_FragCoord.y = render_area_height - gl_FragCoord.y" + // + // When we're rendering to a texture, we want our output to be equivalent to: + // "gl_FragCoord.y = gl_FragCoord.y;" + // + // If we set scale_factor = ((1.0f + dx_ViewScale.y) / 2.0f), then notice that + // - When rendering to default FB: scale_factor = 1.0f + // - When rendering to texture: scale_factor = 0.0f + // + // Therefore, we can get our desired output by setting: + // "gl_FragCoord.y = scale_factor * render_area_height - dx_ViewScale.y * + // gl_FragCoord.y" + // + // Simplifying, this becomes: + pixelStream + << " gl_FragCoord.y = (1.0f + dx_ViewScale.y) * gl_FragCoord.y /" + "(1.0f - input.gl_FragCoord.y * rhw) - dx_ViewScale.y * gl_FragCoord.y;\n"; + } + } + pixelStream << " gl_FragCoord.z = (input.gl_FragCoord.z * rhw) * dx_DepthFront.x + " "dx_DepthFront.y;\n" << " gl_FragCoord.w = rhw;\n"; @@ -749,6 +835,7 @@ std::string DynamicHLSL::generateGeometryShaderPreamble(const VaryingPacking &va std::string DynamicHLSL::generateGeometryShaderHLSL(gl::PrimitiveType primitiveType, const gl::Data &data, const gl::Program::Data &programData, + const bool useViewScale, const std::string &preambleString) const { ASSERT(mRenderer->getMajorShaderModel() >= 4); @@ -799,8 +886,14 @@ std::string DynamicHLSL::generateGeometryShaderHLSL(gl::PrimitiveType primitiveT { shaderStream << "#define ANGLE_POINT_SPRITE_SHADER\n" "\n" - "uniform float4 dx_ViewCoords : register(c1);\n" - "\n" + "uniform float4 dx_ViewCoords : register(c1);\n"; + + if (useViewScale) + { + shaderStream << "uniform float2 dx_ViewScale : register(c3);\n"; + } + + shaderStream << "\n" "static float2 pointSpriteCorners[] = \n" "{\n" " float2( 0.5f, -0.5f),\n" @@ -870,9 +963,20 @@ std::string DynamicHLSL::generateGeometryShaderHLSL(gl::PrimitiveType primitiveT for (int corner = 0; corner < 4; corner++) { - shaderStream << "\n" - " output.dx_Position = dx_Position + float4(pointSpriteCorners[" - << corner << "] * viewportScale * gl_PointSize, 0.0f, 0.0f);\n"; + if (useViewScale) + { + shaderStream << " \n" + " output.dx_Position = dx_Position + float4(1.0f, " + "-dx_ViewScale.y, 1.0f, 1.0f)" + " * float4(pointSpriteCorners[" + << corner << "] * viewportScale * gl_PointSize, 0.0f, 0.0f);\n"; + } + else + { + shaderStream << "\n" + " output.dx_Position = dx_Position + float4(pointSpriteCorners[" + << corner << "] * viewportScale * gl_PointSize, 0.0f, 0.0f);\n"; + } if (usesPointCoord) { diff --git a/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.h b/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.h index df52d245cf..69d941c06a 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.h @@ -74,6 +74,7 @@ class DynamicHLSL : angle::NonCopyable std::string generateGeometryShaderHLSL(gl::PrimitiveType primitiveType, const gl::Data &data, const gl::Program::Data &programData, + const bool useViewScale, const std::string &preambleString) const; void getPixelShaderOutputKey(const gl::Data &data, diff --git a/gfx/angle/src/libANGLE/renderer/d3d/FramebufferD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/FramebufferD3D.cpp index c41b3a6133..82967aced0 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/FramebufferD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/FramebufferD3D.cpp @@ -8,6 +8,7 @@ #include "libANGLE/renderer/d3d/FramebufferD3D.h" +#include "common/BitSetIterator.h" #include "libANGLE/formatutils.h" #include "libANGLE/Framebuffer.h" #include "libANGLE/FramebufferAttachment.h" @@ -53,7 +54,7 @@ ClearParameters GetClearParameters(const gl::State &state, GLbitfield mask) const gl::Framebuffer *framebufferObject = state.getDrawFramebuffer(); if (mask & GL_COLOR_BUFFER_BIT) { - if (framebufferObject->hasEnabledColorAttachment()) + if (framebufferObject->hasEnabledDrawBuffer()) { for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++) { @@ -84,10 +85,8 @@ ClearParameters GetClearParameters(const gl::State &state, GLbitfield mask) } -FramebufferD3D::FramebufferD3D(const gl::Framebuffer::Data &data) - : FramebufferImpl(data), - mColorAttachmentsForRender(mData.getColorAttachments().size(), nullptr), - mInvalidateColorAttachmentCache(true) +FramebufferD3D::FramebufferD3D(const gl::Framebuffer::Data &data, RendererD3D *renderer) + : FramebufferImpl(data), mRenderer(renderer) { } @@ -95,43 +94,20 @@ FramebufferD3D::~FramebufferD3D() { } -void FramebufferD3D::onUpdateColorAttachment(size_t /*index*/) -{ - mInvalidateColorAttachmentCache = true; -} - -void FramebufferD3D::onUpdateDepthAttachment() -{ -} - -void FramebufferD3D::onUpdateStencilAttachment() -{ -} - -void FramebufferD3D::onUpdateDepthStencilAttachment() -{ -} - -void FramebufferD3D::setDrawBuffers(size_t, const GLenum *) -{ - mInvalidateColorAttachmentCache = true; -} - -void FramebufferD3D::setReadBuffer(GLenum) -{ -} - gl::Error FramebufferD3D::clear(const gl::Data &data, GLbitfield mask) { const gl::State &state = *data.state; ClearParameters clearParams = GetClearParameters(state, mask); - return clear(state, clearParams); + return clear(data, clearParams); } -gl::Error FramebufferD3D::clearBufferfv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLfloat *values) +gl::Error FramebufferD3D::clearBufferfv(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + const GLfloat *values) { // glClearBufferfv can be called to clear the color buffer or depth buffer - ClearParameters clearParams = GetClearParameters(state, 0); + ClearParameters clearParams = GetClearParameters(*data.state, 0); if (buffer == GL_COLOR) { @@ -149,13 +125,16 @@ gl::Error FramebufferD3D::clearBufferfv(const gl::State &state, GLenum buffer, G clearParams.depthClearValue = values[0]; } - return clear(state, clearParams); + return clear(data, clearParams); } -gl::Error FramebufferD3D::clearBufferuiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLuint *values) +gl::Error FramebufferD3D::clearBufferuiv(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + const GLuint *values) { // glClearBufferuiv can only be called to clear a color buffer - ClearParameters clearParams = GetClearParameters(state, 0); + ClearParameters clearParams = GetClearParameters(*data.state, 0); for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++) { clearParams.clearColor[i] = (drawbuffer == static_cast(i)); @@ -163,13 +142,16 @@ gl::Error FramebufferD3D::clearBufferuiv(const gl::State &state, GLenum buffer, clearParams.colorUIClearValue = gl::ColorUI(values[0], values[1], values[2], values[3]); clearParams.colorClearType = GL_UNSIGNED_INT; - return clear(state, clearParams); + return clear(data, clearParams); } -gl::Error FramebufferD3D::clearBufferiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLint *values) +gl::Error FramebufferD3D::clearBufferiv(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + const GLint *values) { // glClearBufferiv can be called to clear the color buffer or stencil buffer - ClearParameters clearParams = GetClearParameters(state, 0); + ClearParameters clearParams = GetClearParameters(*data.state, 0); if (buffer == GL_COLOR) { @@ -187,19 +169,23 @@ gl::Error FramebufferD3D::clearBufferiv(const gl::State &state, GLenum buffer, G clearParams.stencilClearValue = values[1]; } - return clear(state, clearParams); + return clear(data, clearParams); } -gl::Error FramebufferD3D::clearBufferfi(const gl::State &state, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) +gl::Error FramebufferD3D::clearBufferfi(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + GLfloat depth, + GLint stencil) { // glClearBufferfi can only be called to clear a depth stencil buffer - ClearParameters clearParams = GetClearParameters(state, 0); + ClearParameters clearParams = GetClearParameters(*data.state, 0); clearParams.clearDepth = true; clearParams.depthClearValue = depth; clearParams.clearStencil = true; clearParams.stencilClearValue = stencil; - return clear(state, clearParams); + return clear(data, clearParams); } GLenum FramebufferD3D::getImplementationColorReadFormat() const @@ -302,14 +288,14 @@ gl::Error FramebufferD3D::blit(const gl::State &state, const gl::Rectangle &sour return gl::Error(GL_NO_ERROR); } -GLenum FramebufferD3D::checkStatus() const +bool FramebufferD3D::checkStatus() const { // if we have both a depth and stencil buffer, they must refer to the same object // since we only support packed_depth_stencil and not separate depth and stencil if (mData.getDepthAttachment() != nullptr && mData.getStencilAttachment() != nullptr && mData.getDepthStencilAttachment() == nullptr) { - return GL_FRAMEBUFFER_UNSUPPORTED; + return false; } // D3D11 does not allow for overlapping RenderTargetViews, so ensure uniqueness @@ -326,28 +312,51 @@ GLenum FramebufferD3D::checkStatus() const (attachment.id() == prevAttachment.id() && attachment.type() == prevAttachment.type())) { - return GL_FRAMEBUFFER_UNSUPPORTED; + return false; } } } } - return GL_FRAMEBUFFER_COMPLETE; + // D3D requires all render targets to have the same dimensions. + if (!mData.attachmentsHaveSameDimensions()) + { + return false; + } + + return true; } -const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender( - const WorkaroundsD3D &workarounds) const +void FramebufferD3D::syncState(const gl::Framebuffer::DirtyBits &dirtyBits) { - if (!mInvalidateColorAttachmentCache) + bool invalidateColorAttachmentCache = false; + + if (!mColorAttachmentsForRender.valid()) { - return mColorAttachmentsForRender; + invalidateColorAttachmentCache = true; + } + + for (auto dirtyBit : angle::IterateBitSet(dirtyBits)) + { + if ((dirtyBit >= gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 && + dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX) || + dirtyBit == gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS) + { + invalidateColorAttachmentCache = true; + } + } + + if (!invalidateColorAttachmentCache) + { + return; } // Does not actually free memory - mColorAttachmentsForRender.clear(); + gl::AttachmentList colorAttachmentsForRender; const auto &colorAttachments = mData.getColorAttachments(); const auto &drawBufferStates = mData.getDrawBufferStates(); + const auto &workarounds = mRenderer->getWorkarounds(); for (size_t attachmentIndex = 0; attachmentIndex < colorAttachments.size(); ++attachmentIndex) { @@ -357,16 +366,21 @@ const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender( if (colorAttachment.isAttached() && drawBufferState != GL_NONE) { ASSERT(drawBufferState == GL_BACK || drawBufferState == (GL_COLOR_ATTACHMENT0_EXT + attachmentIndex)); - mColorAttachmentsForRender.push_back(&colorAttachment); + colorAttachmentsForRender.push_back(&colorAttachment); } else if (!workarounds.mrtPerfWorkaround) { - mColorAttachmentsForRender.push_back(nullptr); + colorAttachmentsForRender.push_back(nullptr); } } - mInvalidateColorAttachmentCache = false; - return mColorAttachmentsForRender; + mColorAttachmentsForRender = std::move(colorAttachmentsForRender); } +const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender() const +{ + ASSERT(mColorAttachmentsForRender.valid()); + return mColorAttachmentsForRender.value(); } + +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/FramebufferD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/FramebufferD3D.h index ff51db0fb1..eb839c4364 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/FramebufferD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/FramebufferD3D.h @@ -12,6 +12,7 @@ #include #include +#include "common/Optional.h" #include "libANGLE/angletypes.h" #include "libANGLE/renderer/FramebufferImpl.h" @@ -26,6 +27,7 @@ typedef std::vector AttachmentList; namespace rx { +class RendererD3D; class RenderTargetD3D; struct WorkaroundsD3D; @@ -55,22 +57,27 @@ struct ClearParameters class FramebufferD3D : public FramebufferImpl { public: - FramebufferD3D(const gl::Framebuffer::Data &data); + FramebufferD3D(const gl::Framebuffer::Data &data, RendererD3D *renderer); virtual ~FramebufferD3D(); - void onUpdateColorAttachment(size_t index) override; - void onUpdateDepthAttachment() override; - void onUpdateStencilAttachment() override; - void onUpdateDepthStencilAttachment() override; - - void setDrawBuffers(size_t count, const GLenum *buffers) override; - void setReadBuffer(GLenum buffer) override; - gl::Error clear(const gl::Data &data, GLbitfield mask) override; - gl::Error clearBufferfv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLfloat *values) override; - gl::Error clearBufferuiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLuint *values) override; - gl::Error clearBufferiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLint *values) override; - gl::Error clearBufferfi(const gl::State &state, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) override; + gl::Error clearBufferfv(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + const GLfloat *values) override; + gl::Error clearBufferuiv(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + const GLuint *values) override; + gl::Error clearBufferiv(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + const GLint *values) override; + gl::Error clearBufferfi(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + GLfloat depth, + GLint stencil) override; GLenum getImplementationColorReadFormat() const override; GLenum getImplementationColorReadType() const override; @@ -79,17 +86,14 @@ class FramebufferD3D : public FramebufferImpl gl::Error blit(const gl::State &state, const gl::Rectangle &sourceArea, const gl::Rectangle &destArea, GLbitfield mask, GLenum filter, const gl::Framebuffer *sourceFramebuffer) override; - GLenum checkStatus() const override; + bool checkStatus() const override; - const gl::AttachmentList &getColorAttachmentsForRender(const WorkaroundsD3D &workarounds) const; + void syncState(const gl::Framebuffer::DirtyBits &dirtyBits) override; - protected: - // Cache variable - mutable gl::AttachmentList mColorAttachmentsForRender; - mutable bool mInvalidateColorAttachmentCache; + const gl::AttachmentList &getColorAttachmentsForRender() const; private: - virtual gl::Error clear(const gl::State &state, const ClearParameters &clearParams) = 0; + virtual gl::Error clear(const gl::Data &data, const ClearParameters &clearParams) = 0; virtual gl::Error readPixelsImpl(const gl::Rectangle &area, GLenum format, @@ -103,6 +107,9 @@ class FramebufferD3D : public FramebufferImpl const gl::Framebuffer *sourceFramebuffer) = 0; virtual GLenum getRenderTargetImplementationFormat(RenderTargetD3D *renderTarget) const = 0; + + RendererD3D *mRenderer; + Optional mColorAttachmentsForRender; }; } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/ImageD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/ImageD3D.cpp index 7d7088b37e..ead5db6453 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/ImageD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/ImageD3D.cpp @@ -29,20 +29,4 @@ ImageD3D::ImageD3D() { } -gl::Error ImageD3D::copy(const gl::Offset &destOffset, const gl::Rectangle &sourceArea, const gl::Framebuffer *source) -{ - const gl::FramebufferAttachment *srcAttachment = source->getReadColorbuffer(); - ASSERT(srcAttachment); - - RenderTargetD3D *renderTarget = NULL; - gl::Error error = srcAttachment->getRenderTarget(&renderTarget); - if (error.isError()) - { - return error; - } - - ASSERT(renderTarget); - return copy(destOffset, sourceArea, renderTarget); -} - -} +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/ImageD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/ImageD3D.h index 6dec5052ca..2afe1cfabf 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/ImageD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/ImageD3D.h @@ -60,13 +60,11 @@ class ImageD3D : angle::NonCopyable virtual gl::Error setManagedSurface2DArray(TextureStorage *storage, int layer, int level) { return gl::Error(GL_NO_ERROR); }; virtual gl::Error copyToStorage(TextureStorage *storage, const gl::ImageIndex &index, const gl::Box ®ion) = 0; - virtual gl::Error copy(const gl::Offset &destOffset, const gl::Box &sourceArea, - const gl::ImageIndex &sourceIndex, TextureStorage *source) = 0; - - gl::Error copy(const gl::Offset &destOffset, const gl::Rectangle &sourceArea, const gl::Framebuffer *source); - virtual gl::Error copy(const gl::Offset &destOffset, - const gl::Rectangle &sourceArea, - RenderTargetD3D *source) = 0; + virtual gl::Error copyFromTexStorage(const gl::ImageIndex &imageIndex, + TextureStorage *source) = 0; + virtual gl::Error copyFromFramebuffer(const gl::Offset &destOffset, + const gl::Rectangle &sourceArea, + const gl::Framebuffer *source) = 0; protected: GLsizei mWidth; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.cpp b/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.cpp index c18ffe1639..f1ba3d3db0 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.cpp @@ -150,7 +150,6 @@ gl::Error IndexDataManager::prepareIndexData(GLenum srcType, gl::Buffer *glBuffer, const GLvoid *indices, TranslatedIndexData *translated, - SourceIndexData *sourceData, bool primitiveRestartFixedIndexEnabled) { // Avoid D3D11's primitive restart index value @@ -176,13 +175,10 @@ gl::Error IndexDataManager::prepareIndexData(GLenum srcType, BufferD3D *buffer = glBuffer ? GetImplAs(glBuffer) : nullptr; translated->indexType = dstType; - if (sourceData) - { - sourceData->srcBuffer = buffer; - sourceData->srcIndices = indices; - sourceData->srcIndexType = srcType; - sourceData->srcCount = count; - } + translated->srcIndexData.srcBuffer = buffer; + translated->srcIndexData.srcIndices = indices; + translated->srcIndexData.srcIndexType = srcType; + translated->srcIndexData.srcCount = count; // Case 1: the indices are passed by pointer, which forces the streaming of index data if (glBuffer == nullptr) diff --git a/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.h b/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.h index c3741cfe3f..44eb68c071 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.h @@ -36,6 +36,15 @@ class IndexBuffer; class BufferD3D; class RendererD3D; +struct SourceIndexData +{ + BufferD3D *srcBuffer; + const GLvoid *srcIndices; + unsigned int srcCount; + GLenum srcIndexType; + bool srcIndicesChanged; +}; + struct TranslatedIndexData { gl::IndexRange indexRange; @@ -46,15 +55,8 @@ struct TranslatedIndexData BufferD3D *storage; GLenum indexType; unsigned int serial; -}; -struct SourceIndexData -{ - BufferD3D *srcBuffer; - const GLvoid *srcIndices; - unsigned int srcCount; - GLenum srcIndexType; - bool srcIndicesChanged; + SourceIndexData srcIndexData; }; class IndexDataManager : angle::NonCopyable @@ -68,7 +70,6 @@ class IndexDataManager : angle::NonCopyable gl::Buffer *glBuffer, const GLvoid *indices, TranslatedIndexData *translated, - SourceIndexData *sourceData, bool primitiveRestartFixedIndexEnabled); private: diff --git a/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp index 7b43a9e050..72c6f1a1a9 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp @@ -401,11 +401,13 @@ D3DVarying::D3DVarying(const std::string &semanticNameIn, ProgramD3DMetadata::ProgramD3DMetadata(int rendererMajorShaderModel, const std::string &shaderModelSuffix, bool usesInstancedPointSpriteEmulation, + bool usesViewScale, const ShaderD3D *vertexShader, const ShaderD3D *fragmentShader) : mRendererMajorShaderModel(rendererMajorShaderModel), mShaderModelSuffix(shaderModelSuffix), mUsesInstancedPointSpriteEmulation(usesInstancedPointSpriteEmulation), + mUsesViewScale(usesViewScale), mVertexShader(vertexShader), mFragmentShader(fragmentShader) { @@ -423,8 +425,7 @@ bool ProgramD3DMetadata::usesBroadcast(const gl::Data &data) const bool ProgramD3DMetadata::usesFragDepth(const gl::Program::Data &programData) const { - // TODO(jmadill): Rename this or check if we need it for version 300 - return (getMajorShaderVersion() < 300 && mFragmentShader->usesFragDepth()); + return mFragmentShader->usesFragDepth(); } bool ProgramD3DMetadata::usesPointCoord() const @@ -447,6 +448,11 @@ bool ProgramD3DMetadata::usesInsertedPointCoordValue() const return !usesPointSize() && usesPointCoord() && mRendererMajorShaderModel >= 4; } +bool ProgramD3DMetadata::usesViewScale() const +{ + return mUsesViewScale; +} + bool ProgramD3DMetadata::addsPointCoordToVertexShader() const { // Instanced PointSprite emulation requires that gl_PointCoord is present in the vertex shader @@ -1105,14 +1111,17 @@ gl::Error ProgramD3D::save(gl::BinaryOutputStream *stream) return gl::Error(GL_NO_ERROR); } +void ProgramD3D::setBinaryRetrievableHint(bool /* retrievable */) +{ +} + gl::Error ProgramD3D::getPixelExecutableForFramebuffer(const gl::Framebuffer *fbo, ShaderExecutableD3D **outExecutable) { mPixelShaderOutputFormatCache.clear(); const FramebufferD3D *fboD3D = GetImplAs(fbo); - const gl::AttachmentList &colorbuffers = - fboD3D->getColorAttachmentsForRender(mRenderer->getWorkarounds()); + const gl::AttachmentList &colorbuffers = fboD3D->getColorAttachmentsForRender(); for (size_t colorAttachment = 0; colorAttachment < colorbuffers.size(); ++colorAttachment) { @@ -1239,9 +1248,8 @@ gl::Error ProgramD3D::getGeometryExecutableForPrimitiveType(const gl::Data &data *outExecutable = nullptr; } - // We only uses a geometry shader for point sprite emulation, or for fixing the provoking - // vertex problem. Otherwise, return a null shader. - if (drawMode != GL_POINTS && !mUsesFlatInterpolation) + // Return a null shader if the current rendering doesn't use a geometry shader + if (!usesGeometryShader(drawMode)) { return gl::Error(GL_NO_ERROR); } @@ -1258,7 +1266,8 @@ gl::Error ProgramD3D::getGeometryExecutableForPrimitiveType(const gl::Data &data } std::string geometryHLSL = mDynamicHLSL->generateGeometryShaderHLSL( - geometryShaderType, data, mData, mGeometryShaderPreamble); + geometryShaderType, data, mData, mRenderer->presentPathFastEnabled(), + mGeometryShaderPreamble); gl::InfoLog tempInfoLog; gl::InfoLog *currentInfoLog = infoLog ? infoLog : &tempInfoLog; @@ -1386,7 +1395,8 @@ LinkResult ProgramD3D::link(const gl::Data &data, gl::InfoLog &infoLog) } ProgramD3DMetadata metadata(mRenderer->getMajorShaderModel(), mRenderer->getShaderModelSuffix(), - usesInstancedPointSpriteEmulation(), vertexShaderD3D, + usesInstancedPointSpriteEmulation(), + mRenderer->presentPathFastEnabled(), vertexShaderD3D, fragmentShaderD3D); varyingPacking.enableBuiltins(SHADER_VERTEX, metadata); @@ -1550,7 +1560,7 @@ void ProgramD3D::initializeUniformStorage() gl::Error ProgramD3D::applyUniforms(GLenum drawMode) { - updateSamplerMapping(); + ASSERT(!mDirtySamplerMapping); gl::Error error = mRenderer->applyUniforms(*this, drawMode, mD3DUniforms); if (error.isError()) @@ -2053,8 +2063,8 @@ size_t ProgramD3D::getUniformBlockInfo(const sh::InterfaceBlock &interfaceBlock) encoder = &hlslEncoder; } - GetUniformBlockInfo(interfaceBlock.fields, "", encoder, interfaceBlock.isRowMajorLayout, - &mBlockInfo); + GetUniformBlockInfo(interfaceBlock.fields, interfaceBlock.fieldPrefix(), encoder, + interfaceBlock.isRowMajorLayout, &mBlockInfo); return encoder->getBlockSize(); } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.h index b5eea9b373..3dfe52db1c 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.h @@ -103,6 +103,7 @@ class ProgramD3DMetadata : angle::NonCopyable ProgramD3DMetadata(int rendererMajorShaderModel, const std::string &shaderModelSuffix, bool usesInstancedPointSpriteEmulation, + bool usesViewScale, const ShaderD3D *vertexShader, const ShaderD3D *fragmentShader); @@ -113,6 +114,7 @@ class ProgramD3DMetadata : angle::NonCopyable bool usesFragCoord() const; bool usesPointSize() const; bool usesInsertedPointCoordValue() const; + bool usesViewScale() const; bool addsPointCoordToVertexShader() const; bool usesTransformFeedbackGLPosition() const; bool usesSystemValuePointSize() const; @@ -124,6 +126,7 @@ class ProgramD3DMetadata : angle::NonCopyable const int mRendererMajorShaderModel; const std::string mShaderModelSuffix; const bool mUsesInstancedPointSpriteEmulation; + const bool mUsesViewScale; const ShaderD3D *mVertexShader; const ShaderD3D *mFragmentShader; }; @@ -150,8 +153,9 @@ class ProgramD3D : public ProgramImpl bool usesGeometryShader(GLenum drawMode) const; bool usesInstancedPointSpriteEmulation() const; - LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream); - gl::Error save(gl::BinaryOutputStream *stream); + LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) override; + gl::Error save(gl::BinaryOutputStream *stream) override; + void setBinaryRetrievableHint(bool retrievable) override; gl::Error getPixelExecutableForFramebuffer(const gl::Framebuffer *fbo, ShaderExecutableD3D **outExectuable); @@ -244,6 +248,8 @@ class ProgramD3D : public ProgramImpl void updateCachedInputLayout(const gl::State &state); const gl::InputLayout &getCachedInputLayout() const { return mCachedInputLayout; } + bool isSamplerMappingDirty() { return mDirtySamplerMapping; } + private: class VertexExecutable { diff --git a/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.cpp index 849ee8ba89..105587f62c 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.cpp @@ -16,7 +16,6 @@ #include "libANGLE/Framebuffer.h" #include "libANGLE/FramebufferAttachment.h" #include "libANGLE/renderer/d3d/BufferD3D.h" -#include "libANGLE/renderer/d3d/CompilerD3D.h" #include "libANGLE/renderer/d3d/DeviceD3D.h" #include "libANGLE/renderer/d3d/DisplayD3D.h" #include "libANGLE/renderer/d3d/IndexDataManager.h" @@ -38,15 +37,14 @@ const int ScratchMemoryBufferLifetime = 1000; } // anonymous namespace -const uintptr_t RendererD3D::DirtyPointer = std::numeric_limits::max(); - RendererD3D::RendererD3D(egl::Display *display) : mDisplay(display), mDeviceLost(false), mAnnotator(nullptr), + mPresentPathFastEnabled(false), mScratchMemoryBufferResetCounter(0), mWorkaroundsInitialized(false), - mEGLDevice(nullptr) + mDisjoint(false) { } @@ -57,8 +55,6 @@ RendererD3D::~RendererD3D() void RendererD3D::cleanup() { - SafeDelete(mEGLDevice); - mScratchMemoryBuffer.resize(0); for (auto &incompleteTexture : mIncompleteTextures) { @@ -73,11 +69,6 @@ void RendererD3D::cleanup() } } -CompilerImpl *RendererD3D::createCompiler() -{ - return new CompilerD3D(getRendererClass()); -} - SamplerImpl *RendererD3D::createSampler() { return new SamplerD3D(); @@ -156,13 +147,7 @@ gl::Error RendererD3D::genericDrawElements(const gl::Data &data, return gl::Error(GL_NO_ERROR); } - error = applyRenderTarget(data, mode, false); - if (error.isError()) - { - return error; - } - - error = applyState(data, mode); + error = updateState(data, mode); if (error.isError()) { return error; @@ -171,9 +156,7 @@ gl::Error RendererD3D::genericDrawElements(const gl::Data &data, TranslatedIndexData indexInfo; indexInfo.indexRange = indexRange; - SourceIndexData sourceIndexInfo; - - error = applyIndexBuffer(data, indices, count, mode, type, &indexInfo, &sourceIndexInfo); + error = applyIndexBuffer(data, indices, count, mode, type, &indexInfo); if (error.isError()) { return error; @@ -186,13 +169,7 @@ gl::Error RendererD3D::genericDrawElements(const gl::Data &data, size_t vertexCount = indexInfo.indexRange.vertexCount(); error = applyVertexBuffer(*data.state, mode, static_cast(indexInfo.indexRange.start), - static_cast(vertexCount), instances, &sourceIndexInfo); - if (error.isError()) - { - return error; - } - - error = applyShaders(data, mode); + static_cast(vertexCount), instances, &indexInfo); if (error.isError()) { return error; @@ -204,6 +181,12 @@ gl::Error RendererD3D::genericDrawElements(const gl::Data &data, return error; } + error = applyShaders(data, mode); + if (error.isError()) + { + return error; + } + error = programD3D->applyUniformBuffers(data); if (error.isError()) { @@ -246,13 +229,7 @@ gl::Error RendererD3D::genericDrawArrays(const gl::Data &data, return gl::Error(GL_NO_ERROR); } - error = applyRenderTarget(data, mode, false); - if (error.isError()) - { - return error; - } - - error = applyState(data, mode); + error = updateState(data, mode); if (error.isError()) { return error; @@ -266,13 +243,13 @@ gl::Error RendererD3D::genericDrawArrays(const gl::Data &data, return error; } - error = applyShaders(data, mode); + error = applyTextures(data); if (error.isError()) { return error; } - error = applyTextures(data); + error = applyShaders(data, mode); if (error.isError()) { return error; @@ -346,45 +323,8 @@ gl::Error RendererD3D::generateSwizzles(const gl::Data &data) return gl::Error(GL_NO_ERROR); } -// Applies the render target surface, depth stencil surface, viewport rectangle and -// scissor rectangle to the renderer -gl::Error RendererD3D::applyRenderTarget(const gl::Data &data, GLenum drawMode, bool ignoreViewport) +unsigned int RendererD3D::GetBlendSampleMask(const gl::Data &data, int samples) { - const gl::Framebuffer *framebufferObject = data.state->getDrawFramebuffer(); - ASSERT(framebufferObject && framebufferObject->checkStatus(data) == GL_FRAMEBUFFER_COMPLETE); - - gl::Error error = applyRenderTarget(framebufferObject); - if (error.isError()) - { - return error; - } - - float nearZ = data.state->getNearPlane(); - float farZ = data.state->getFarPlane(); - setViewport(data.state->getViewport(), nearZ, farZ, drawMode, - data.state->getRasterizerState().frontFace, ignoreViewport); - - setScissorRectangle(data.state->getScissor(), data.state->isScissorTestEnabled()); - - return gl::Error(GL_NO_ERROR); -} - -// Applies the fixed-function state (culling, depth test, alpha blending, stenciling, etc) to the Direct3D device -gl::Error RendererD3D::applyState(const gl::Data &data, GLenum drawMode) -{ - const gl::Framebuffer *framebufferObject = data.state->getDrawFramebuffer(); - int samples = framebufferObject->getSamples(data); - - gl::RasterizerState rasterizer = data.state->getRasterizerState(); - rasterizer.pointDrawMode = (drawMode == GL_POINTS); - rasterizer.multiSample = (samples != 0); - - gl::Error error = setRasterizerState(rasterizer); - if (error.isError()) - { - return error; - } - unsigned int mask = 0; if (data.state->isSampleCoverageEnabled()) { @@ -415,21 +355,8 @@ gl::Error RendererD3D::applyState(const gl::Data &data, GLenum drawMode) { mask = 0xFFFFFFFF; } - error = setBlendState(framebufferObject, data.state->getBlendState(), - data.state->getBlendColor(), mask); - if (error.isError()) - { - return error; - } - error = setDepthStencilState(data.state->getDepthStencilState(), data.state->getStencilRef(), - data.state->getStencilBackRef(), rasterizer.frontFace == GL_CCW); - if (error.isError()) - { - return error; - } - - return gl::Error(GL_NO_ERROR); + return mask; } // Applies the shaders and shader constants to the Direct3D device @@ -451,11 +378,14 @@ gl::Error RendererD3D::applyShaders(const gl::Data &data, GLenum drawMode) // For each Direct3D sampler of either the pixel or vertex stage, // looks up the corresponding OpenGL texture image unit and texture type, // and sets the texture and its addressing/filtering state (or NULL when inactive). +// Sampler mapping needs to be up-to-date on the program object before this is called. gl::Error RendererD3D::applyTextures(const gl::Data &data, gl::SamplerType shaderType, const FramebufferTextureArray &framebufferTextures, size_t framebufferTextureCount) { ProgramD3D *programD3D = GetImplAs(data.state->getProgram()); + ASSERT(!programD3D->isSamplerMappingDirty()); + unsigned int samplerRange = programD3D->getUsedSamplerRange(shaderType); for (unsigned int samplerIndex = 0; samplerIndex < samplerRange; samplerIndex++) { @@ -492,7 +422,15 @@ gl::Error RendererD3D::applyTextures(const gl::Data &data, gl::SamplerType shade { // Texture is not sampler complete or it is in use by the framebuffer. Bind the incomplete texture. gl::Texture *incompleteTexture = getIncompleteTexture(textureType); - gl::Error error = setTexture(shaderType, samplerIndex, incompleteTexture); + + gl::Error error = setSamplerState(shaderType, samplerIndex, incompleteTexture, + incompleteTexture->getSamplerState()); + if (error.isError()) + { + return error; + } + + error = setTexture(shaderType, samplerIndex, incompleteTexture); if (error.isError()) { return error; @@ -725,6 +663,31 @@ void RendererD3D::popGroupMarker() getAnnotator()->endEvent(); } +void RendererD3D::setGPUDisjoint() +{ + mDisjoint = true; +} + +GLint RendererD3D::getGPUDisjoint() +{ + bool disjoint = mDisjoint; + + // Disjoint flag is cleared when read + mDisjoint = false; + + return disjoint; +} + +GLint64 RendererD3D::getTimestamp() +{ + // D3D has no way to get an actual timestamp reliably so 0 is returned + return 0; +} + +void RendererD3D::onMakeCurrent(const gl::Data &data) +{ +} + void RendererD3D::initializeDebugAnnotator() { createAnnotator(); @@ -737,17 +700,4 @@ gl::DebugAnnotator *RendererD3D::getAnnotator() ASSERT(mAnnotator); return mAnnotator; } - -egl::Error RendererD3D::getEGLDevice(DeviceImpl **device) -{ - egl::Error error = initializeEGLDevice(&mEGLDevice); - if (error.isError()) - { - return error; - } - - *device = static_cast(mEGLDevice); - - return egl::Error(EGL_SUCCESS); -} } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.h index 37cc310723..f956f037e2 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.h @@ -133,8 +133,6 @@ class RendererD3D : public Renderer, public BufferFactoryD3D bool isDeviceLost() const override; std::string getVendorString() const override; - CompilerImpl *createCompiler() override; - SamplerImpl *createSampler() override; virtual int getMinorShaderModel() const = 0; @@ -143,7 +141,11 @@ class RendererD3D : public Renderer, public BufferFactoryD3D // Direct3D Specific methods virtual DeviceIdentifier getAdapterIdentifier() const = 0; - virtual SwapChainD3D *createSwapChain(NativeWindow nativeWindow, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat) = 0; + virtual SwapChainD3D *createSwapChain(NativeWindow nativeWindow, + HANDLE shareHandle, + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation) = 0; virtual gl::Error generateSwizzle(gl::Texture *texture) = 0; virtual gl::Error setSamplerState(gl::SamplerType type, int index, gl::Texture *texture, const gl::SamplerState &sampler) = 0; @@ -153,36 +155,27 @@ class RendererD3D : public Renderer, public BufferFactoryD3D const std::vector &vertexUniformBuffers, const std::vector &fragmentUniformBuffers) = 0; - virtual gl::Error setRasterizerState(const gl::RasterizerState &rasterState) = 0; - virtual gl::Error setBlendState(const gl::Framebuffer *framebuffer, - const gl::BlendState &blendState, - const gl::ColorF &blendColor, - unsigned int sampleMask) = 0; - - virtual gl::Error setDepthStencilState(const gl::DepthStencilState &depthStencilState, int stencilRef, - int stencilBackRef, bool frontFaceCCW) = 0; - - virtual void setScissorRectangle(const gl::Rectangle &scissor, bool enabled) = 0; - virtual void setViewport(const gl::Rectangle &viewport, float zNear, float zFar, GLenum drawMode, GLenum frontFace, - bool ignoreViewport) = 0; + virtual gl::Error updateState(const gl::Data &data, GLenum drawMode) = 0; virtual gl::Error applyRenderTarget(const gl::Framebuffer *frameBuffer) = 0; virtual gl::Error applyUniforms(const ProgramD3D &programD3D, GLenum drawMode, const std::vector &uniformArray) = 0; virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount, bool usesPointSize) = 0; - virtual gl::Error applyVertexBuffer(const gl::State &state, GLenum mode, GLint first, GLsizei count, GLsizei instances, SourceIndexData *sourceIndexInfo) = 0; + virtual gl::Error applyVertexBuffer(const gl::State &state, + GLenum mode, + GLint first, + GLsizei count, + GLsizei instances, + TranslatedIndexData *indexInfo) = 0; virtual gl::Error applyIndexBuffer(const gl::Data &data, const GLvoid *indices, GLsizei count, GLenum mode, GLenum type, - TranslatedIndexData *indexInfo, - SourceIndexData *sourceIndexInfo) = 0; + TranslatedIndexData *indexInfo) = 0; virtual void applyTransformFeedbackBuffers(const gl::State& state) = 0; - virtual void markAllStateDirty() = 0; - virtual unsigned int getReservedVertexUniformVectors() const = 0; virtual unsigned int getReservedFragmentUniformVectors() const = 0; virtual unsigned int getReservedVertexUniformBuffers() const = 0; @@ -252,10 +245,19 @@ class RendererD3D : public Renderer, public BufferFactoryD3D void pushGroupMarker(GLsizei length, const char *marker) override; void popGroupMarker() override; + void setGPUDisjoint(); + + GLint getGPUDisjoint() override; + GLint64 getTimestamp() override; + + void onMakeCurrent(const gl::Data &data) override; + // In D3D11, faster than calling setTexture a jillion times virtual gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd) = 0; - egl::Error getEGLDevice(DeviceImpl **device); + virtual egl::Error getEGLDevice(DeviceImpl **device) = 0; + + bool presentPathFastEnabled() const { return mPresentPathFastEnabled; } protected: virtual bool getLUID(LUID *adapterLuid) const = 0; @@ -265,10 +267,8 @@ class RendererD3D : public Renderer, public BufferFactoryD3D virtual void createAnnotator() = 0; - virtual egl::Error initializeEGLDevice(DeviceD3D **outDevice) = 0; - + static unsigned int GetBlendSampleMask(const gl::Data &data, int samples); // dirtyPointer is a special value that will make the comparison with any valid pointer fail and force the renderer to re-apply the state. - static const uintptr_t DirtyPointer; egl::Display *mDisplay; bool mDeviceLost; @@ -278,6 +278,8 @@ class RendererD3D : public Renderer, public BufferFactoryD3D std::vector mTranslatedAttribCache; + bool mPresentPathFastEnabled; + private: gl::Error genericDrawArrays(const gl::Data &data, GLenum mode, @@ -311,7 +313,6 @@ class RendererD3D : public Renderer, public BufferFactoryD3D gl::Error generateSwizzles(const gl::Data &data, gl::SamplerType type); gl::Error generateSwizzles(const gl::Data &data); - gl::Error applyRenderTarget(const gl::Data &data, GLenum drawMode, bool ignoreViewport); gl::Error applyState(const gl::Data &data, GLenum drawMode); gl::Error applyShaders(const gl::Data &data, GLenum drawMode); gl::Error applyTextures(const gl::Data &data, gl::SamplerType shaderType, @@ -335,21 +336,7 @@ class RendererD3D : public Renderer, public BufferFactoryD3D mutable bool mWorkaroundsInitialized; mutable WorkaroundsD3D mWorkarounds; - DeviceD3D *mEGLDevice; -}; - -struct dx_VertexConstants -{ - float depthRange[4]; - float viewAdjust[4]; - float viewCoords[4]; -}; - -struct dx_PixelConstants -{ - float depthRange[4]; - float viewCoords[4]; - float depthFront[4]; + bool mDisjoint; }; } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp index 77685f06d7..93f69904a3 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp @@ -24,13 +24,22 @@ namespace rx SurfaceD3D *SurfaceD3D::createOffscreen(RendererD3D *renderer, egl::Display *display, const egl::Config *config, EGLClientBuffer shareHandle, EGLint width, EGLint height) { - return new SurfaceD3D(renderer, display, config, width, height, EGL_TRUE, shareHandle, NULL); + return new SurfaceD3D(renderer, display, config, width, height, EGL_TRUE, 0, EGL_FALSE, + shareHandle, NULL); } -SurfaceD3D *SurfaceD3D::createFromWindow(RendererD3D *renderer, egl::Display *display, const egl::Config *config, EGLNativeWindowType window, - EGLint fixedSize, EGLint width, EGLint height) +SurfaceD3D *SurfaceD3D::createFromWindow(RendererD3D *renderer, + egl::Display *display, + const egl::Config *config, + EGLNativeWindowType window, + EGLint fixedSize, + EGLint directComposition, + EGLint width, + EGLint height, + EGLint orientation) { - return new SurfaceD3D(renderer, display, config, width, height, fixedSize, static_cast(0), window); + return new SurfaceD3D(renderer, display, config, width, height, fixedSize, orientation, + directComposition, static_cast(0), window); } SurfaceD3D::SurfaceD3D(RendererD3D *renderer, @@ -39,17 +48,20 @@ SurfaceD3D::SurfaceD3D(RendererD3D *renderer, EGLint width, EGLint height, EGLint fixedSize, + EGLint orientation, + EGLint directComposition, EGLClientBuffer shareHandle, EGLNativeWindowType window) : SurfaceImpl(), mRenderer(renderer), mDisplay(display), mFixedSize(fixedSize == EGL_TRUE), + mOrientation(orientation), mRenderTargetFormat(config->renderTargetFormat), mDepthStencilFormat(config->depthStencilFormat), mSwapChain(nullptr), mSwapIntervalDirty(true), - mNativeWindow(window, config), + mNativeWindow(window, config, directComposition == EGL_TRUE), mWidth(width), mHeight(height), mSwapInterval(1), @@ -91,7 +103,7 @@ FramebufferImpl *SurfaceD3D::createDefaultFramebuffer(const gl::Framebuffer::Dat return mRenderer->createFramebuffer(data); } -egl::Error SurfaceD3D::bindTexImage(EGLint) +egl::Error SurfaceD3D::bindTexImage(gl::Texture *, EGLint) { return egl::Error(EGL_SUCCESS); } @@ -128,7 +140,8 @@ egl::Error SurfaceD3D::resetSwapChain() height = mHeight; } - mSwapChain = mRenderer->createSwapChain(mNativeWindow, mShareHandle, mRenderTargetFormat, mDepthStencilFormat); + mSwapChain = mRenderer->createSwapChain(mNativeWindow, mShareHandle, mRenderTargetFormat, + mDepthStencilFormat, mOrientation); if (!mSwapChain) { return egl::Error(EGL_BAD_ALLOC); diff --git a/gfx/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h index e6f727aeac..b925bfc8cc 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h @@ -25,8 +25,15 @@ class RendererD3D; class SurfaceD3D : public SurfaceImpl { public: - static SurfaceD3D *createFromWindow(RendererD3D *renderer, egl::Display *display, const egl::Config *config, - EGLNativeWindowType window, EGLint fixedSize, EGLint width, EGLint height); + static SurfaceD3D *createFromWindow(RendererD3D *renderer, + egl::Display *display, + const egl::Config *config, + EGLNativeWindowType window, + EGLint fixedSize, + EGLint directComposition, + EGLint width, + EGLint height, + EGLint orientation); static SurfaceD3D *createOffscreen(RendererD3D *renderer, egl::Display *display, const egl::Config *config, EGLClientBuffer shareHandle, EGLint width, EGLint height); ~SurfaceD3D() override; @@ -38,7 +45,7 @@ class SurfaceD3D : public SurfaceImpl egl::Error swap() override; egl::Error postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) override; egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override; - egl::Error bindTexImage(EGLint buffer) override; + egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override; egl::Error releaseTexImage(EGLint buffer) override; void setSwapInterval(EGLint interval) override; @@ -60,8 +67,16 @@ class SurfaceD3D : public SurfaceImpl FramebufferAttachmentRenderTarget **rtOut) override; private: - SurfaceD3D(RendererD3D *renderer, egl::Display *display, const egl::Config *config, EGLint width, EGLint height, - EGLint fixedSize, EGLClientBuffer shareHandle, EGLNativeWindowType window); + SurfaceD3D(RendererD3D *renderer, + egl::Display *display, + const egl::Config *config, + EGLint width, + EGLint height, + EGLint fixedSize, + EGLint orientation, + EGLint directComposition, + EGLClientBuffer shareHandle, + EGLNativeWindowType window); egl::Error swapRect(EGLint x, EGLint y, EGLint width, EGLint height); egl::Error resetSwapChain(int backbufferWidth, int backbufferHeight); @@ -71,6 +86,7 @@ class SurfaceD3D : public SurfaceImpl egl::Display *mDisplay; bool mFixedSize; + GLint mOrientation; GLenum mRenderTargetFormat; GLenum mDepthStencilFormat; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp index ee367c60d6..430576b318 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp @@ -141,9 +141,6 @@ GLenum TextureD3D::getBaseLevelInternalFormat() const bool TextureD3D::shouldUseSetData(const ImageD3D *image) const { - if (image->isDirty()) - return false; - if (!mRenderer->getWorkarounds().setDataFasterThanImageUpload) { return false; @@ -453,9 +450,7 @@ gl::Error TextureD3D::generateMipmapsUsingImages() gl::ImageIndex srcIndex = getImageIndex(0, layer); ImageD3D *image = getImage(srcIndex); - gl::Box area(0, 0, 0, image->getWidth(), image->getHeight(), image->getDepth()); - gl::Offset offset(0, 0, 0); - gl::Error error = image->copy(offset, area, srcIndex, mTexStorage); + gl::Error error = image->copyFromTexStorage(srcIndex, mTexStorage); if (error.isError()) { return error; @@ -830,7 +825,7 @@ gl::Error TextureD3D_2D::copyImage(GLenum target, // so we should use the non-rendering copy path. if (!canCreateRenderTargetForImage(index) || mRenderer->getWorkarounds().zeroMaxLodWorkaround) { - gl::Error error = mImageArray[level]->copy(destOffset, sourceArea, source); + gl::Error error = mImageArray[level]->copyFromFramebuffer(destOffset, sourceArea, source); if (error.isError()) { return error; @@ -879,7 +874,7 @@ gl::Error TextureD3D_2D::copySubImage(GLenum target, // so we should use the non-rendering copy path. if (!canCreateRenderTargetForImage(index) || mRenderer->getWorkarounds().zeroMaxLodWorkaround) { - gl::Error error = mImageArray[level]->copy(destOffset, sourceArea, source); + gl::Error error = mImageArray[level]->copyFromFramebuffer(destOffset, sourceArea, source); if (error.isError()) { return error; @@ -1257,16 +1252,8 @@ void TextureD3D_2D::redefineImage(size_t level, // while orphaning if (level != 0 && mEGLImageTarget) { - gl::Offset offset(0, 0, 0); - gl::Rectangle sourceArea(0, 0, mImageArray[0]->getWidth(), mImageArray[0]->getHeight()); - - RenderTargetD3D *storageRendertarget = nullptr; - gl::Error error = - mTexStorage->getRenderTarget(gl::ImageIndex::Make2D(0), &storageRendertarget); - if (!error.isError()) - { - mImageArray[0]->copy(offset, sourceArea, storageRendertarget); - } + // TODO(jmadill): Don't discard error. + mImageArray[0]->copyFromTexStorage(gl::ImageIndex::Make2D(0), mTexStorage); } if ((level >= storageLevels && storageLevels != 0) || @@ -1445,7 +1432,8 @@ gl::Error TextureD3D_Cube::copyImage(GLenum target, // so we should use the non-rendering copy path. if (!canCreateRenderTargetForImage(index) || mRenderer->getWorkarounds().zeroMaxLodWorkaround) { - gl::Error error = mImageArray[faceIndex][level]->copy(destOffset, sourceArea, source); + gl::Error error = + mImageArray[faceIndex][level]->copyFromFramebuffer(destOffset, sourceArea, source); if (error.isError()) { return error; @@ -1493,7 +1481,8 @@ gl::Error TextureD3D_Cube::copySubImage(GLenum target, // so we should use the non-rendering copy path. if (!canCreateRenderTargetForImage(index) || mRenderer->getWorkarounds().zeroMaxLodWorkaround) { - gl::Error error = mImageArray[faceIndex][level]->copy(destOffset, sourceArea, source); + gl::Error error = + mImageArray[faceIndex][level]->copyFromFramebuffer(destOffset, sourceArea, source); if (error.isError()) { return error; @@ -2115,7 +2104,7 @@ gl::Error TextureD3D_3D::copySubImage(GLenum target, if (canCreateRenderTargetForImage(index)) { - gl::Error error = mImageArray[level]->copy(destOffset, sourceArea, source); + gl::Error error = mImageArray[level]->copyFromFramebuffer(destOffset, sourceArea, source); if (error.isError()) { return error; @@ -2685,7 +2674,8 @@ gl::Error TextureD3D_2DArray::copySubImage(GLenum target, if (canCreateRenderTargetForImage(index)) { gl::Offset destLayerOffset(destOffset.x, destOffset.y, 0); - gl::Error error = mImageArray[level][destOffset.z]->copy(destLayerOffset, sourceArea, source); + gl::Error error = mImageArray[level][destOffset.z]->copyFromFramebuffer(destLayerOffset, + sourceArea, source); if (error.isError()) { return error; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp index 0c1ce233d9..e951e13408 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp @@ -196,6 +196,23 @@ inline unsigned int GetSwizzleIndex(GLenum swizzle) return colorIndex; } +D3D11_BLEND_DESC GetAlphaMaskBlendStateDesc() +{ + D3D11_BLEND_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.RenderTarget[0].BlendEnable = TRUE; + desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + desc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; + desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO; + desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_RED | + D3D11_COLOR_WRITE_ENABLE_GREEN | + D3D11_COLOR_WRITE_ENABLE_BLUE; + return desc; +} + D3D11_INPUT_ELEMENT_DESC quad2DLayout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, @@ -236,6 +253,7 @@ Blit11::Blit11(Renderer11 *renderer) "Blit11 3D input layout"), mQuad3DVS(g_VS_Passthrough3D, ArraySize(g_VS_Passthrough3D), "Blit11 3D vertex shader"), mQuad3DGS(g_GS_Passthrough3D, ArraySize(g_GS_Passthrough3D), "Blit11 3D geometry shader"), + mAlphaMaskBlendState(GetAlphaMaskBlendStateDesc(), "Blit11 Alpha Mask Blend"), mSwizzleCB(nullptr) { } @@ -691,7 +709,8 @@ gl::Error Blit11::swizzleTexture(ID3D11ShaderResourceView *source, deviceContext->GSSetShader(support.geometryShader, nullptr, 0); // Unset the currently bound shader resource to avoid conflicts - mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, nullptr); + auto stateManager = mRenderer->getStateManager(); + stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, nullptr); // Apply render target mRenderer->setOneTimeRenderTarget(dest); @@ -707,7 +726,7 @@ gl::Error Blit11::swizzleTexture(ID3D11ShaderResourceView *source, deviceContext->RSSetViewports(1, &viewport); // Apply textures - mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, source); + stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, source); // Apply samplers deviceContext->PSSetSamplers(0, 1, &mPointSampler); @@ -716,7 +735,7 @@ gl::Error Blit11::swizzleTexture(ID3D11ShaderResourceView *source, deviceContext->Draw(drawCount, 0); // Unbind textures and render targets and vertex buffer - mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, nullptr); + stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, nullptr); mRenderer->unapplyRenderTargets(); @@ -729,9 +748,16 @@ gl::Error Blit11::swizzleTexture(ID3D11ShaderResourceView *source, return gl::Error(GL_NO_ERROR); } -gl::Error Blit11::copyTexture(ID3D11ShaderResourceView *source, const gl::Box &sourceArea, const gl::Extents &sourceSize, - ID3D11RenderTargetView *dest, const gl::Box &destArea, const gl::Extents &destSize, - const gl::Rectangle *scissor, GLenum destFormat, GLenum filter) +gl::Error Blit11::copyTexture(ID3D11ShaderResourceView *source, + const gl::Box &sourceArea, + const gl::Extents &sourceSize, + ID3D11RenderTargetView *dest, + const gl::Box &destArea, + const gl::Extents &destSize, + const gl::Rectangle *scissor, + GLenum destFormat, + GLenum filter, + bool maskOffAlpha) { gl::Error error = initResources(); if (error.isError()) @@ -784,7 +810,16 @@ gl::Error Blit11::copyTexture(ID3D11ShaderResourceView *source, const gl::Box &s deviceContext->IASetVertexBuffers(0, 1, &mVertexBuffer, &stride, &startIdx); // Apply state - deviceContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFF); + if (maskOffAlpha) + { + ID3D11BlendState *blendState = mAlphaMaskBlendState.resolve(mRenderer->getDevice()); + ASSERT(blendState); + deviceContext->OMSetBlendState(blendState, nullptr, 0xFFFFFFF); + } + else + { + deviceContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFF); + } deviceContext->OMSetDepthStencilState(nullptr, 0xFFFFFFFF); if (scissor) @@ -812,7 +847,8 @@ gl::Error Blit11::copyTexture(ID3D11ShaderResourceView *source, const gl::Box &s deviceContext->GSSetShader(support.geometryShader, nullptr, 0); // Unset the currently bound shader resource to avoid conflicts - mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, nullptr); + auto stateManager = mRenderer->getStateManager(); + stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, nullptr); // Apply render target mRenderer->setOneTimeRenderTarget(dest); @@ -828,7 +864,7 @@ gl::Error Blit11::copyTexture(ID3D11ShaderResourceView *source, const gl::Box &s deviceContext->RSSetViewports(1, &viewport); // Apply textures - mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, source); + stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, source); // Apply samplers ID3D11SamplerState *sampler = nullptr; @@ -847,7 +883,7 @@ gl::Error Blit11::copyTexture(ID3D11ShaderResourceView *source, const gl::Box &s deviceContext->Draw(drawCount, 0); // Unbind textures and render targets and vertex buffer - mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, nullptr); + stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, nullptr); mRenderer->unapplyRenderTargets(); @@ -939,7 +975,8 @@ gl::Error Blit11::copyDepth(ID3D11ShaderResourceView *source, const gl::Box &sou deviceContext->GSSetShader(nullptr, nullptr, 0); // Unset the currently bound shader resource to avoid conflicts - mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, nullptr); + auto stateManager = mRenderer->getStateManager(); + stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, nullptr); // Apply render target deviceContext->OMSetRenderTargets(0, nullptr, dest); @@ -955,7 +992,7 @@ gl::Error Blit11::copyDepth(ID3D11ShaderResourceView *source, const gl::Box &sou deviceContext->RSSetViewports(1, &viewport); // Apply textures - mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, source); + stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, source); // Apply samplers deviceContext->PSSetSamplers(0, 1, &mPointSampler); @@ -964,7 +1001,7 @@ gl::Error Blit11::copyDepth(ID3D11ShaderResourceView *source, const gl::Box &sou deviceContext->Draw(drawCount, 0); // Unbind textures and render targets and vertex buffer - mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, nullptr); + stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, nullptr); mRenderer->unapplyRenderTargets(); diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.h index b64183a581..906616131e 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.h @@ -29,9 +29,16 @@ class Blit11 : angle::NonCopyable gl::Error swizzleTexture(ID3D11ShaderResourceView *source, ID3D11RenderTargetView *dest, const gl::Extents &size, GLenum swizzleRed, GLenum swizzleGreen, GLenum swizzleBlue, GLenum swizzleAlpha); - gl::Error copyTexture(ID3D11ShaderResourceView *source, const gl::Box &sourceArea, const gl::Extents &sourceSize, - ID3D11RenderTargetView *dest, const gl::Box &destArea, const gl::Extents &destSize, - const gl::Rectangle *scissor, GLenum destFormat, GLenum filter); + gl::Error copyTexture(ID3D11ShaderResourceView *source, + const gl::Box &sourceArea, + const gl::Extents &sourceSize, + ID3D11RenderTargetView *dest, + const gl::Box &destArea, + const gl::Extents &destSize, + const gl::Rectangle *scissor, + GLenum destFormat, + GLenum filter, + bool maskOffAlpha); gl::Error copyStencil(ID3D11Resource *source, unsigned int sourceSubresource, const gl::Box &sourceArea, const gl::Extents &sourceSize, ID3D11Resource *dest, unsigned int destSubresource, const gl::Box &destArea, const gl::Extents &destSize, @@ -167,6 +174,8 @@ class Blit11 : angle::NonCopyable d3d11::LazyShader mQuad3DVS; d3d11::LazyShader mQuad3DGS; + d3d11::LazyBlendState mAlphaMaskBlendState; + ID3D11Buffer *mSwizzleCB; }; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp index 7ce85f2a0a..66e2d67e71 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp @@ -8,10 +8,13 @@ #include "libANGLE/renderer/d3d/d3d11/Buffer11.h" +#include + #include "common/MemoryBuffer.h" #include "libANGLE/renderer/d3d/IndexDataManager.h" #include "libANGLE/renderer/d3d/VertexDataManager.h" #include "libANGLE/renderer/d3d/d3d11/Renderer11.h" +#include "libANGLE/renderer/d3d/d3d11/RenderTarget11.h" #include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h" #include "libANGLE/renderer/d3d/d3d11/formatutils11.h" @@ -199,18 +202,15 @@ class Buffer11::PackStorage : public Buffer11::BufferStorage uint8_t *map(size_t offset, size_t length, GLbitfield access) override; void unmap() override; - gl::Error packPixels(ID3D11Texture2D *srcTexure, - UINT srcSubresource, + gl::Error packPixels(const gl::FramebufferAttachment &readAttachment, const PackPixelsParams ¶ms); private: gl::Error flushQueuedPackCommand(); - ID3D11Texture2D *mStagingTexture; - DXGI_FORMAT mTextureFormat; - gl::Extents mTextureSize; + TextureHelper11 mStagingTexture; MemoryBuffer mMemoryBuffer; - PackPixelsParams *mQueuedPackCommand; + std::unique_ptr mQueuedPackCommand; PackPixelsParams mPackParams; bool mDataModified; }; @@ -624,8 +624,7 @@ ID3D11ShaderResourceView *Buffer11::getSRV(DXGI_FORMAT srvFormat) return bufferSRV; } -gl::Error Buffer11::packPixels(ID3D11Texture2D *srcTexture, - UINT srcSubresource, +gl::Error Buffer11::packPixels(const gl::FramebufferAttachment &readAttachment, const PackPixelsParams ¶ms) { PackStorage *packStorage = getPackStorage(); @@ -633,7 +632,7 @@ gl::Error Buffer11::packPixels(ID3D11Texture2D *srcTexture, if (packStorage) { - gl::Error error = packStorage->packPixels(srcTexture, srcSubresource, params); + gl::Error error = packStorage->packPixels(readAttachment, params); if (error.isError()) { return error; @@ -1245,18 +1244,12 @@ void Buffer11::EmulatedIndexedStorage::unmap() } Buffer11::PackStorage::PackStorage(Renderer11 *renderer) - : BufferStorage(renderer, BUFFER_USAGE_PIXEL_PACK), - mStagingTexture(nullptr), - mTextureFormat(DXGI_FORMAT_UNKNOWN), - mQueuedPackCommand(nullptr), - mDataModified(false) + : BufferStorage(renderer, BUFFER_USAGE_PIXEL_PACK), mStagingTexture(), mDataModified(false) { } Buffer11::PackStorage::~PackStorage() { - SafeRelease(mStagingTexture); - SafeDelete(mQueuedPackCommand); } bool Buffer11::PackStorage::copyFromStorage(BufferStorage *source, @@ -1308,8 +1301,7 @@ void Buffer11::PackStorage::unmap() // No-op } -gl::Error Buffer11::PackStorage::packPixels(ID3D11Texture2D *srcTexure, - UINT srcSubresource, +gl::Error Buffer11::PackStorage::packPixels(const gl::FramebufferAttachment &readAttachment, const PackPixelsParams ¶ms) { gl::Error error = flushQueuedPackCommand(); @@ -1318,53 +1310,37 @@ gl::Error Buffer11::PackStorage::packPixels(ID3D11Texture2D *srcTexure, return error; } - mQueuedPackCommand = new PackPixelsParams(params); - - D3D11_TEXTURE2D_DESC textureDesc; - srcTexure->GetDesc(&textureDesc); - - if (mStagingTexture != nullptr && - (mTextureFormat != textureDesc.Format || mTextureSize.width != params.area.width || - mTextureSize.height != params.area.height)) + RenderTarget11 *renderTarget = nullptr; + error = readAttachment.getRenderTarget(&renderTarget); + if (error.isError()) { - SafeRelease(mStagingTexture); - mTextureSize.width = 0; - mTextureSize.height = 0; - mTextureFormat = DXGI_FORMAT_UNKNOWN; + return error; } - if (mStagingTexture == nullptr) + ID3D11Resource *renderTargetResource = renderTarget->getTexture(); + ASSERT(renderTargetResource); + + unsigned int srcSubresource = renderTarget->getSubresourceIndex(); + TextureHelper11 srcTexture = TextureHelper11::MakeAndReference(renderTargetResource); + + mQueuedPackCommand.reset(new PackPixelsParams(params)); + + gl::Extents srcTextureSize(params.area.width, params.area.height, 1); + if (!mStagingTexture.getResource() || mStagingTexture.getFormat() != srcTexture.getFormat() || + mStagingTexture.getExtents() != srcTextureSize) { - ID3D11Device *device = mRenderer->getDevice(); - HRESULT hr; - - mTextureSize.width = params.area.width; - mTextureSize.height = params.area.height; - mTextureFormat = textureDesc.Format; - - D3D11_TEXTURE2D_DESC stagingDesc; - stagingDesc.Width = params.area.width; - stagingDesc.Height = params.area.height; - stagingDesc.MipLevels = 1; - stagingDesc.ArraySize = 1; - stagingDesc.Format = mTextureFormat; - stagingDesc.SampleDesc.Count = 1; - stagingDesc.SampleDesc.Quality = 0; - stagingDesc.Usage = D3D11_USAGE_STAGING; - stagingDesc.BindFlags = 0; - stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - stagingDesc.MiscFlags = 0; - - hr = device->CreateTexture2D(&stagingDesc, nullptr, &mStagingTexture); - if (FAILED(hr)) + auto textureOrError = + CreateStagingTexture(srcTexture.getTextureType(), srcTexture.getFormat(), + srcTextureSize, mRenderer->getDevice()); + if (textureOrError.isError()) { - ASSERT(hr == E_OUTOFMEMORY); - return gl::Error(GL_OUT_OF_MEMORY, "Failed to allocate internal staging texture."); + return textureOrError.getError(); } + mStagingTexture = std::move(textureOrError.getResult()); } // ReadPixels from multisampled FBOs isn't supported in current GL - ASSERT(textureDesc.SampleDesc.Count <= 1); + ASSERT(srcTexture.getSampleCount() <= 1); ID3D11DeviceContext *immediateContext = mRenderer->getDeviceContext(); D3D11_BOX srcBox; @@ -1372,12 +1348,18 @@ gl::Error Buffer11::PackStorage::packPixels(ID3D11Texture2D *srcTexure, srcBox.right = params.area.x + params.area.width; srcBox.top = params.area.y; srcBox.bottom = params.area.y + params.area.height; - srcBox.front = 0; - srcBox.back = 1; + + // Select the correct layer from a 3D attachment + srcBox.front = 0; + if (mStagingTexture.getTextureType() == GL_TEXTURE_3D) + { + srcBox.front = static_cast(readAttachment.layer()); + } + srcBox.back = srcBox.front + 1; // Asynchronous copy - immediateContext->CopySubresourceRegion(mStagingTexture, 0, 0, 0, 0, srcTexure, srcSubresource, - &srcBox); + immediateContext->CopySubresourceRegion(mStagingTexture.getResource(), 0, 0, 0, 0, + srcTexture.getResource(), srcSubresource, &srcBox); return gl::Error(GL_NO_ERROR); } @@ -1390,7 +1372,7 @@ gl::Error Buffer11::PackStorage::flushQueuedPackCommand() { gl::Error error = mRenderer->packPixels(mStagingTexture, *mQueuedPackCommand, mMemoryBuffer.data()); - SafeDelete(mQueuedPackCommand); + mQueuedPackCommand.reset(nullptr); if (error.isError()) { return error; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h index 4a2027d3a1..a748db57ae 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h @@ -14,6 +14,11 @@ #include "libANGLE/angletypes.h" #include "libANGLE/renderer/d3d/BufferD3D.h" +namespace gl +{ +class FramebufferAttachment; +} + namespace rx { class Renderer11; @@ -62,7 +67,8 @@ class Buffer11 : public BufferD3D ID3D11Buffer *getConstantBufferRange(GLintptr offset, GLsizeiptr size); ID3D11ShaderResourceView *getSRV(DXGI_FORMAT srvFormat); bool isMapped() const { return mMappedStorage != NULL; } - gl::Error packPixels(ID3D11Texture2D *srcTexure, UINT srcSubresource, const PackPixelsParams ¶ms); + gl::Error packPixels(const gl::FramebufferAttachment &readAttachment, + const PackPixelsParams ¶ms); size_t getTotalCPUBufferMemoryBytes() const; // BufferD3D implementation diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Clear11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Clear11.cpp index 9adef8a59e..03b28c2a56 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Clear11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Clear11.cpp @@ -229,21 +229,15 @@ gl::Error Clear11::clearFramebuffer(const ClearParameters &clearParams, const gl const gl::FramebufferAttachment *colorAttachment = fboData.getFirstColorAttachment(); if (colorAttachment != nullptr) { - framebufferSize.width = colorAttachment->getWidth(); - framebufferSize.height = colorAttachment->getHeight(); - framebufferSize.depth = 1; + framebufferSize = colorAttachment->getSize(); } else if (depthAttachment != nullptr) { - framebufferSize.width = depthAttachment->getWidth(); - framebufferSize.height = depthAttachment->getHeight(); - framebufferSize.depth = 1; + framebufferSize = depthAttachment->getSize(); } else if (stencilAttachment != nullptr) { - framebufferSize.width = stencilAttachment->getWidth(); - framebufferSize.height = stencilAttachment->getHeight(); - framebufferSize.depth = 1; + framebufferSize = stencilAttachment->getSize(); } else { diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp index cedcc085d2..beffa30955 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp @@ -25,8 +25,7 @@ namespace rx { Framebuffer11::Framebuffer11(const gl::Framebuffer::Data &data, Renderer11 *renderer) - : FramebufferD3D(data), - mRenderer(renderer) + : FramebufferD3D(data, renderer), mRenderer(renderer) { ASSERT(mRenderer != nullptr); } @@ -91,10 +90,30 @@ gl::Error Framebuffer11::invalidateSwizzles() const return gl::Error(GL_NO_ERROR); } -gl::Error Framebuffer11::clear(const gl::State &state, const ClearParameters &clearParams) +gl::Error Framebuffer11::clear(const gl::Data &data, const ClearParameters &clearParams) { Clear11 *clearer = mRenderer->getClearer(); - gl::Error error = clearer->clearFramebuffer(clearParams, mData); + gl::Error error(GL_NO_ERROR); + + const gl::FramebufferAttachment *colorAttachment = mData.getFirstColorAttachment(); + if (clearParams.scissorEnabled == true && colorAttachment != nullptr && + UsePresentPathFast(mRenderer, colorAttachment)) + { + // If the current framebuffer is using the default colorbuffer, and present path fast is + // active, and the scissor rect is enabled, then we should invert the scissor rect + // vertically + ClearParameters presentPathFastClearParams = clearParams; + gl::Extents framebufferSize = colorAttachment->getSize(); + presentPathFastClearParams.scissor.y = framebufferSize.height - + presentPathFastClearParams.scissor.y - + presentPathFastClearParams.scissor.height; + error = clearer->clearFramebuffer(presentPathFastClearParams, mData); + } + else + { + error = clearer->clearFramebuffer(clearParams, mData); + } + if (error.isError()) { return error; @@ -109,32 +128,6 @@ gl::Error Framebuffer11::clear(const gl::State &state, const ClearParameters &cl return gl::Error(GL_NO_ERROR); } -static gl::Error getRenderTargetResource(const gl::FramebufferAttachment *colorbuffer, unsigned int *subresourceIndexOut, - ID3D11Texture2D **texture2DOut) -{ - ASSERT(colorbuffer); - - RenderTarget11 *renderTarget = nullptr; - gl::Error error = colorbuffer->getRenderTarget(&renderTarget); - if (error.isError()) - { - return error; - } - - ID3D11Resource *renderTargetResource = renderTarget->getTexture(); - ASSERT(renderTargetResource); - - *subresourceIndexOut = renderTarget->getSubresourceIndex(); - *texture2DOut = d3d11::DynamicCastComObject(renderTargetResource); - - if (!(*texture2DOut)) - { - return gl::Error(GL_OUT_OF_MEMORY, "Failed to query the ID3D11Texture2D from a RenderTarget"); - } - - return gl::Error(GL_NO_ERROR); -} - gl::Error Framebuffer11::invalidate(size_t count, const GLenum *attachments) { return invalidateBase(count, attachments, false); @@ -296,17 +289,8 @@ gl::Error Framebuffer11::readPixelsImpl(const gl::Rectangle &area, const gl::PixelPackState &pack, uint8_t *pixels) const { - ID3D11Texture2D *colorBufferTexture = nullptr; - unsigned int subresourceIndex = 0; - - const gl::FramebufferAttachment *colorbuffer = mData.getReadAttachment(); - ASSERT(colorbuffer); - - gl::Error error = getRenderTargetResource(colorbuffer, &subresourceIndex, &colorBufferTexture); - if (error.isError()) - { - return error; - } + const gl::FramebufferAttachment *readAttachment = mData.getReadAttachment(); + ASSERT(readAttachment); gl::Buffer *packBuffer = pack.pixelBuffer.get(); if (packBuffer != nullptr) @@ -322,27 +306,11 @@ gl::Error Framebuffer11::readPixelsImpl(const gl::Rectangle &area, PackPixelsParams packParams(area, format, type, static_cast(outputPitch), pack, reinterpret_cast(pixels)); - error = packBufferStorage->packPixels(colorBufferTexture, subresourceIndex, packParams); - if (error.isError()) - { - SafeRelease(colorBufferTexture); - return error; - } - } - else - { - error = mRenderer->readTextureData(colorBufferTexture, subresourceIndex, area, format, type, - static_cast(outputPitch), pack, pixels); - if (error.isError()) - { - SafeRelease(colorBufferTexture); - return error; - } + return packBufferStorage->packPixels(*readAttachment, packParams); } - SafeRelease(colorBufferTexture); - - return gl::Error(GL_NO_ERROR); + return mRenderer->readFromAttachment(*readAttachment, area, format, type, + static_cast(outputPitch), pack, pixels); } gl::Error Framebuffer11::blit(const gl::Rectangle &sourceArea, const gl::Rectangle &destArea, const gl::Rectangle *scissor, @@ -380,8 +348,27 @@ gl::Error Framebuffer11::blit(const gl::Rectangle &sourceArea, const gl::Rectang } ASSERT(drawRenderTarget); - error = mRenderer->blitRenderbufferRect(sourceArea, destArea, readRenderTarget, drawRenderTarget, - filter, scissor, blitRenderTarget, false, false); + const bool invertColorSource = UsePresentPathFast(mRenderer, readBuffer); + gl::Rectangle actualSourceArea = sourceArea; + if (invertColorSource) + { + RenderTarget11 *readRenderTarget11 = GetAs(readRenderTarget); + actualSourceArea.y = readRenderTarget11->getHeight() - sourceArea.y; + actualSourceArea.height = -sourceArea.height; + } + + const bool invertColorDest = UsePresentPathFast(mRenderer, &drawBuffer); + gl::Rectangle actualDestArea = destArea; + if (invertColorDest) + { + RenderTarget11 *drawRenderTarget11 = GetAs(drawRenderTarget); + actualDestArea.y = drawRenderTarget11->getHeight() - destArea.y; + actualDestArea.height = -destArea.height; + } + + error = mRenderer->blitRenderbufferRect(actualSourceArea, actualDestArea, + readRenderTarget, drawRenderTarget, filter, + scissor, blitRenderTarget, false, false); if (error.isError()) { return error; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.h index b4b4e03e5f..c8a33ec7e5 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.h @@ -29,7 +29,7 @@ class Framebuffer11 : public FramebufferD3D gl::Error invalidateSwizzles() const; private: - gl::Error clear(const gl::State &state, const ClearParameters &clearParams) override; + gl::Error clear(const gl::Data &data, const ClearParameters &clearParams) override; gl::Error readPixelsImpl(const gl::Rectangle &area, GLenum format, diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Image11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Image11.cpp index 8d5efed2fa..c52092d81e 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Image11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Image11.cpp @@ -256,7 +256,7 @@ gl::Error Image11::loadData(const gl::Box &area, const gl::PixelUnpackState &unp GLuint outputPixelSize = dxgiFormatInfo.pixelBytes; const d3d11::TextureFormat &d3dFormatInfo = d3d11::GetTextureFormatInfo(mInternalFormat, mRenderer->getRenderer11DeviceCaps()); - LoadImageFunction loadFunction = d3dFormatInfo.loadFunctions.at(type); + LoadImageFunction loadFunction = d3dFormatInfo.loadFunctions.at(type).loadFunction; D3D11_MAPPED_SUBRESOURCE mappedImage; gl::Error error = map(D3D11_MAP_WRITE, &mappedImage); @@ -291,7 +291,7 @@ gl::Error Image11::loadCompressedData(const gl::Box &area, const void *input) ASSERT(area.y % outputBlockHeight == 0); const d3d11::TextureFormat &d3dFormatInfo = d3d11::GetTextureFormatInfo(mInternalFormat, mRenderer->getRenderer11DeviceCaps()); - LoadImageFunction loadFunction = d3dFormatInfo.loadFunctions.at(GL_UNSIGNED_BYTE); + LoadImageFunction loadFunction = d3dFormatInfo.loadFunctions.at(GL_UNSIGNED_BYTE).loadFunction; D3D11_MAPPED_SUBRESOURCE mappedImage; gl::Error error = map(D3D11_MAP_WRITE, &mappedImage); @@ -313,180 +313,159 @@ gl::Error Image11::loadCompressedData(const gl::Box &area, const void *input) return gl::Error(GL_NO_ERROR); } -gl::Error Image11::copy(const gl::Offset &destOffset, const gl::Rectangle &sourceArea, RenderTargetD3D *source) +gl::Error Image11::copyFromTexStorage(const gl::ImageIndex &imageIndex, TextureStorage *source) { - RenderTarget11 *sourceRenderTarget = GetAs(source); - ASSERT(sourceRenderTarget->getTexture()); + TextureStorage11 *storage11 = GetAs(source); - ID3D11Resource *resource = sourceRenderTarget->getTexture(); - UINT subresourceIndex = sourceRenderTarget->getSubresourceIndex(); - - gl::Box sourceBox(sourceArea.x, sourceArea.y, 0, sourceArea.width, sourceArea.height, 1); - gl::Error error = copy(destOffset, sourceBox, resource, subresourceIndex); - - SafeRelease(resource); - - return error; -} - -gl::Error Image11::copy(const gl::Offset &destOffset, const gl::Box &sourceArea, const gl::ImageIndex &sourceIndex, TextureStorage *source) -{ - TextureStorage11 *sourceStorage11 = GetAs(source); - - UINT subresourceIndex = sourceStorage11->getSubresourceIndex(sourceIndex); - ID3D11Resource *resource = NULL; - gl::Error error = sourceStorage11->getResource(&resource); + ID3D11Resource *resource = nullptr; + gl::Error error = storage11->getResource(&resource); if (error.isError()) { return error; } - error = copy(destOffset, sourceArea, resource, subresourceIndex); + UINT subresourceIndex = storage11->getSubresourceIndex(imageIndex); + TextureHelper11 textureHelper = TextureHelper11::MakeAndReference(resource); - SafeRelease(resource); + gl::Box sourceBox(0, 0, 0, mWidth, mHeight, mDepth); + return copyWithoutConversion(gl::Offset(), sourceBox, textureHelper, subresourceIndex); +} + +gl::Error Image11::copyFromFramebuffer(const gl::Offset &destOffset, + const gl::Rectangle &sourceArea, + const gl::Framebuffer *sourceFBO) +{ + const gl::FramebufferAttachment *srcAttachment = sourceFBO->getReadColorbuffer(); + ASSERT(srcAttachment); + + const auto &d3d11Format = d3d11::GetTextureFormatInfo(srcAttachment->getInternalFormat(), + mRenderer->getRenderer11DeviceCaps()); + + if (d3d11Format.texFormat == mDXGIFormat) + { + RenderTargetD3D *renderTarget = nullptr; + gl::Error error = srcAttachment->getRenderTarget(&renderTarget); + if (error.isError()) + { + return error; + } + + RenderTarget11 *rt11 = GetAs(renderTarget); + ASSERT(rt11->getTexture()); + + TextureHelper11 textureHelper = TextureHelper11::MakeAndReference(rt11->getTexture()); + unsigned int sourceSubResource = rt11->getSubresourceIndex(); + + gl::Box sourceBox(sourceArea.x, sourceArea.y, 0, sourceArea.width, sourceArea.height, 1); + return copyWithoutConversion(destOffset, sourceBox, textureHelper, sourceSubResource); + } + + // This format requires conversion, so we must copy the texture to staging and manually convert + // via readPixels + D3D11_MAPPED_SUBRESOURCE mappedImage; + gl::Error error = map(D3D11_MAP_WRITE, &mappedImage); + if (error.isError()) + { + return error; + } + + // determine the offset coordinate into the destination buffer + const auto &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(mDXGIFormat); + GLsizei rowOffset = dxgiFormatInfo.pixelBytes * destOffset.x; + + uint8_t *dataOffset = static_cast(mappedImage.pData) + + mappedImage.RowPitch * destOffset.y + rowOffset + + destOffset.z * mappedImage.DepthPitch; + + const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(mInternalFormat); + + error = mRenderer->readFromAttachment(*srcAttachment, sourceArea, formatInfo.format, + formatInfo.type, mappedImage.RowPitch, + gl::PixelPackState(), dataOffset); + + unmap(); + mDirty = true; return error; } -gl::Error Image11::copy(const gl::Offset &destOffset, const gl::Box &sourceArea, ID3D11Resource *source, UINT sourceSubResource) +gl::Error Image11::copyWithoutConversion(const gl::Offset &destOffset, + const gl::Box &sourceArea, + const TextureHelper11 &textureHelper, + UINT sourceSubResource) { - D3D11_RESOURCE_DIMENSION dim; - source->GetType(&dim); - - DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; - gl::Extents extents; - UINT sampleCount = 0; - - ID3D11Texture2D *source2D = NULL; - - if (dim == D3D11_RESOURCE_DIMENSION_TEXTURE2D) + // No conversion needed-- use copyback fastpath + ID3D11Resource *stagingTexture = nullptr; + unsigned int stagingSubresourceIndex = 0; + gl::Error error = getStagingTexture(&stagingTexture, &stagingSubresourceIndex); + if (error.isError()) { - D3D11_TEXTURE2D_DESC textureDesc2D; - source2D = d3d11::DynamicCastComObject(source); - ASSERT(source2D); - source2D->GetDesc(&textureDesc2D); - - format = textureDesc2D.Format; - extents = gl::Extents(textureDesc2D.Width, textureDesc2D.Height, 1); - sampleCount = textureDesc2D.SampleDesc.Count; + return error; } - else if (dim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) - { - D3D11_TEXTURE3D_DESC textureDesc3D; - ID3D11Texture3D *source3D = d3d11::DynamicCastComObject(source); - ASSERT(source3D); - source3D->GetDesc(&textureDesc3D); - format = textureDesc3D.Format; - extents = gl::Extents(textureDesc3D.Width, textureDesc3D.Height, textureDesc3D.Depth); - sampleCount = 1; + ID3D11Device *device = mRenderer->getDevice(); + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + + UINT subresourceAfterResolve = sourceSubResource; + + ID3D11Resource *srcTex = nullptr; + const gl::Extents &extents = textureHelper.getExtents(); + + bool needResolve = + (textureHelper.getTextureType() == GL_TEXTURE_2D && textureHelper.getSampleCount() > 1); + + if (needResolve) + { + D3D11_TEXTURE2D_DESC resolveDesc; + resolveDesc.Width = extents.width; + resolveDesc.Height = extents.height; + resolveDesc.MipLevels = 1; + resolveDesc.ArraySize = 1; + resolveDesc.Format = textureHelper.getFormat(); + resolveDesc.SampleDesc.Count = 1; + resolveDesc.SampleDesc.Quality = 0; + resolveDesc.Usage = D3D11_USAGE_DEFAULT; + resolveDesc.BindFlags = 0; + resolveDesc.CPUAccessFlags = 0; + resolveDesc.MiscFlags = 0; + + ID3D11Texture2D *srcTex2D = NULL; + HRESULT result = device->CreateTexture2D(&resolveDesc, NULL, &srcTex2D); + if (FAILED(result)) + { + return gl::Error(GL_OUT_OF_MEMORY, + "Failed to create resolve texture for Image11::copy, HRESULT: 0x%X.", + result); + } + srcTex = srcTex2D; + + deviceContext->ResolveSubresource(srcTex, 0, textureHelper.getTexture2D(), + sourceSubResource, textureHelper.getFormat()); + subresourceAfterResolve = 0; } else { - UNREACHABLE(); + srcTex = textureHelper.getResource(); } - if (format == mDXGIFormat) + D3D11_BOX srcBox; + srcBox.left = sourceArea.x; + srcBox.right = sourceArea.x + sourceArea.width; + srcBox.top = sourceArea.y; + srcBox.bottom = sourceArea.y + sourceArea.height; + srcBox.front = sourceArea.z; + srcBox.back = sourceArea.z + sourceArea.depth; + + deviceContext->CopySubresourceRegion(stagingTexture, stagingSubresourceIndex, destOffset.x, + destOffset.y, destOffset.z, srcTex, + subresourceAfterResolve, &srcBox); + + if (needResolve) { - // No conversion needed-- use copyback fastpath - ID3D11Resource *stagingTexture = NULL; - unsigned int stagingSubresourceIndex = 0; - gl::Error error = getStagingTexture(&stagingTexture, &stagingSubresourceIndex); - if (error.isError()) - { - return error; - } - - ID3D11Device *device = mRenderer->getDevice(); - ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); - - UINT subresourceAfterResolve = sourceSubResource; - - ID3D11Resource *srcTex = NULL; - - bool needResolve = (dim == D3D11_RESOURCE_DIMENSION_TEXTURE2D && sampleCount > 1); - - if (needResolve) - { - D3D11_TEXTURE2D_DESC resolveDesc; - resolveDesc.Width = extents.width; - resolveDesc.Height = extents.height; - resolveDesc.MipLevels = 1; - resolveDesc.ArraySize = 1; - resolveDesc.Format = format; - resolveDesc.SampleDesc.Count = 1; - resolveDesc.SampleDesc.Quality = 0; - resolveDesc.Usage = D3D11_USAGE_DEFAULT; - resolveDesc.BindFlags = 0; - resolveDesc.CPUAccessFlags = 0; - resolveDesc.MiscFlags = 0; - - ID3D11Texture2D *srcTex2D = NULL; - HRESULT result = device->CreateTexture2D(&resolveDesc, NULL, &srcTex2D); - if (FAILED(result)) - { - return gl::Error(GL_OUT_OF_MEMORY, "Failed to create resolve texture for Image11::copy, HRESULT: 0x%X.", result); - } - srcTex = srcTex2D; - - deviceContext->ResolveSubresource(srcTex, 0, source, sourceSubResource, format); - subresourceAfterResolve = 0; - } - else - { - srcTex = source; - } - - D3D11_BOX srcBox; - srcBox.left = sourceArea.x; - srcBox.right = sourceArea.x + sourceArea.width; - srcBox.top = sourceArea.y; - srcBox.bottom = sourceArea.y + sourceArea.height; - srcBox.front = sourceArea.z; - srcBox.back = sourceArea.z + sourceArea.depth; - - deviceContext->CopySubresourceRegion(stagingTexture, stagingSubresourceIndex, destOffset.x, destOffset.y, - destOffset.z, srcTex, subresourceAfterResolve, &srcBox); - - if (needResolve) - { - SafeRelease(srcTex); - } - } - else - { - // This format requires conversion, so we must copy the texture to staging and manually convert via readPixels - D3D11_MAPPED_SUBRESOURCE mappedImage; - gl::Error error = map(D3D11_MAP_WRITE, &mappedImage); - if (error.isError()) - { - return error; - } - - // determine the offset coordinate into the destination buffer - const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(mDXGIFormat); - GLsizei rowOffset = dxgiFormatInfo.pixelBytes * destOffset.x; - uint8_t *dataOffset = static_cast(mappedImage.pData) + mappedImage.RowPitch * destOffset.y + rowOffset + destOffset.z * mappedImage.DepthPitch; - - const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(mInternalFormat); - - // Currently in ANGLE, the source data may only need to be converted if the source is the current framebuffer - // and OpenGL ES framebuffers must be 2D textures therefore we should not need to convert 3D textures between different formats. - ASSERT(dim == D3D11_RESOURCE_DIMENSION_TEXTURE2D); - ASSERT(sourceArea.z == 0 && sourceArea.depth == 1); - gl::Rectangle sourceRect(sourceArea.x, sourceArea.y, sourceArea.width, sourceArea.height); - error = mRenderer->readTextureData(source2D, sourceSubResource, sourceRect, formatInfo.format, formatInfo.type, mappedImage.RowPitch, gl::PixelPackState(), dataOffset); - - unmap(); - - if (error.isError()) - { - return error; - } + SafeRelease(srcTex); } mDirty = true; - return gl::Error(GL_NO_ERROR); } @@ -547,7 +526,7 @@ gl::Error Image11::createStagingTexture() if (d3d11::GetTextureFormatInfo(mInternalFormat, mRenderer->getRenderer11DeviceCaps()).dataInitializerFunction != NULL) { std::vector initialData; - std::vector< std::vector > textureData; + std::vector> textureData; d3d11::GenerateInitialTextureData(mInternalFormat, mRenderer->getRenderer11DeviceCaps(), width, height, mDepth, lodOffset + 1, &initialData, &textureData); @@ -587,7 +566,7 @@ gl::Error Image11::createStagingTexture() if (d3d11::GetTextureFormatInfo(mInternalFormat, mRenderer->getRenderer11DeviceCaps()).dataInitializerFunction != NULL) { std::vector initialData; - std::vector< std::vector > textureData; + std::vector> textureData; d3d11::GenerateInitialTextureData(mInternalFormat, mRenderer->getRenderer11DeviceCaps(), width, height, 1, lodOffset + 1, &initialData, &textureData); @@ -662,4 +641,4 @@ void Image11::unmap() } } -} +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Image11.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Image11.h index 2cc50549f3..a5fcec84f8 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Image11.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Image11.h @@ -23,6 +23,7 @@ class Framebuffer; namespace rx { class Renderer11; +class TextureHelper11; class TextureStorage11; class Image11 : public ImageD3D @@ -44,9 +45,10 @@ class Image11 : public ImageD3D virtual gl::Error loadData(const gl::Box &area, const gl::PixelUnpackState &unpack, GLenum type, const void *input); virtual gl::Error loadCompressedData(const gl::Box &area, const void *input); - virtual gl::Error copy(const gl::Offset &destOffset, const gl::Rectangle &sourceArea, RenderTargetD3D *source); - virtual gl::Error copy(const gl::Offset &destOffset, const gl::Box &sourceArea, - const gl::ImageIndex &sourceIndex, TextureStorage *source); + gl::Error copyFromTexStorage(const gl::ImageIndex &imageIndex, TextureStorage *source) override; + gl::Error copyFromFramebuffer(const gl::Offset &destOffset, + const gl::Rectangle &sourceArea, + const gl::Framebuffer *source) override; gl::Error recoverFromAssociatedStorage(); bool isAssociatedStorageValid(TextureStorage11* textureStorage) const; @@ -57,8 +59,10 @@ class Image11 : public ImageD3D void unmap(); private: - gl::Error copyToStorageImpl(TextureStorage11 *storage11, const gl::ImageIndex &index, const gl::Box ®ion); - gl::Error copy(const gl::Offset &destOffset, const gl::Box &sourceArea, ID3D11Resource *source, UINT sourceSubResource); + gl::Error copyWithoutConversion(const gl::Offset &destOffset, + const gl::Box &sourceArea, + const TextureHelper11 &textureHelper, + UINT sourceSubResource); gl::Error getStagingTexture(ID3D11Resource **outStagingTexture, unsigned int *outSubresourceIndex); gl::Error createStagingTexture(); diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.cpp index aafd598b45..3a6d797ea6 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.cpp @@ -27,9 +27,12 @@ namespace rx namespace { -gl::InputLayout GetInputLayout( - const TranslatedAttribute *translatedAttributes[gl::MAX_VERTEX_ATTRIBS], - size_t attributeCount) +size_t GetReservedBufferCount(bool usesPointSpriteEmulation) +{ + return usesPointSpriteEmulation ? 1 : 0; +} + +gl::InputLayout GetInputLayout(const SortedAttribArray &translatedAttributes, size_t attributeCount) { gl::InputLayout inputLayout(attributeCount, gl::VERTEX_FORMAT_INVALID); @@ -79,6 +82,19 @@ struct PackedAttribute uint8_t divisor; }; +Optional FindFirstNonInstanced(const SortedAttribArray &sortedAttributes, size_t maxIndex) +{ + for (size_t index = 0; index < maxIndex; ++index) + { + if (sortedAttributes[index]->divisor == 0) + { + return Optional(index); + } + } + + return Optional::Invalid(); +} + } // anonymous namespace void InputLayoutCache::PackedAttributeLayout::addAttributeData( @@ -120,13 +136,13 @@ bool InputLayoutCache::PackedAttributeLayout::operator<(const PackedAttributeLay return memcmp(attributeData, other.attributeData, sizeof(uint32_t) * numAttributes) < 0; } -InputLayoutCache::InputLayoutCache() - : mCacheSize(kDefaultCacheSize) +InputLayoutCache::InputLayoutCache() : mUnsortedAttributesCount(0), mCacheSize(kDefaultCacheSize) { mCounter = 0; mDevice = NULL; mDeviceContext = NULL; mCurrentIL = NULL; + for (unsigned int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++) { mCurrentBuffers[i] = NULL; @@ -171,227 +187,73 @@ void InputLayoutCache::markDirty() mCurrentVertexStrides[i] = static_cast(-1); mCurrentVertexOffsets[i] = static_cast(-1); } + mUnsortedAttributesCount = 0; } -gl::Error InputLayoutCache::applyVertexBuffers(const std::vector &unsortedAttributes, - GLenum mode, gl::Program *program, SourceIndexData *sourceInfo) +gl::Error InputLayoutCache::applyVertexBuffers( + const std::vector &unsortedAttributes, + GLenum mode, + gl::Program *program, + TranslatedIndexData *indexInfo, + GLsizei numIndicesPerInstance) { + ASSERT(mDevice && mDeviceContext); + ProgramD3D *programD3D = GetImplAs(program); - int sortedSemanticIndices[gl::MAX_VERTEX_ATTRIBS]; - const TranslatedAttribute *sortedAttributes[gl::MAX_VERTEX_ATTRIBS] = { nullptr }; - programD3D->sortAttributesByLayout(unsortedAttributes, sortedSemanticIndices, sortedAttributes); bool programUsesInstancedPointSprites = programD3D->usesPointSize() && programD3D->usesInstancedPointSpriteEmulation(); bool instancedPointSpritesActive = programUsesInstancedPointSprites && (mode == GL_POINTS); - bool indexedPointSpriteEmulationActive = instancedPointSpritesActive && (sourceInfo != nullptr); - const auto &semanticToLocation = programD3D->getAttributesByLayout(); + SortedIndexArray sortedSemanticIndices; + mSortedAttributes.fill(nullptr); + mUnsortedAttributesCount = unsortedAttributes.size(); - if (!mDevice || !mDeviceContext) + programD3D->sortAttributesByLayout(unsortedAttributes, sortedSemanticIndices.data(), + mSortedAttributes.data()); + + // If we are using FL 9_3, make sure the first attribute is not instanced + if (mFeatureLevel <= D3D_FEATURE_LEVEL_9_3 && !unsortedAttributes.empty()) { - return gl::Error(GL_OUT_OF_MEMORY, "Internal input layout cache is not initialized."); - } - - unsigned int inputElementCount = 0; - D3D11_INPUT_ELEMENT_DESC inputElements[gl::MAX_VERTEX_ATTRIBS]; - PackedAttributeLayout layout; - - static const char* semanticName = "TEXCOORD"; - - unsigned int firstIndexedElement = gl::MAX_VERTEX_ATTRIBS; - unsigned int firstInstancedElement = gl::MAX_VERTEX_ATTRIBS; - unsigned int nextAvailableInputSlot = 0; - - const std::vector &shaderAttributes = program->getAttributes(); - - for (unsigned int i = 0; i < unsortedAttributes.size(); i++) - { - if (sortedAttributes[i]->active) + if (mSortedAttributes[0]->divisor > 0) { - D3D11_INPUT_CLASSIFICATION inputClass = sortedAttributes[i]->divisor > 0 ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA; - // If rendering points and instanced pointsprite emulation is being used, the inputClass is required to be configured as per instance data - inputClass = instancedPointSpritesActive ? D3D11_INPUT_PER_INSTANCE_DATA : inputClass; - - gl::VertexFormatType vertexFormatType = gl::GetVertexFormatType(*sortedAttributes[i]->attribute, sortedAttributes[i]->currentValueType); - const d3d11::VertexFormat &vertexFormatInfo = d3d11::GetVertexFormatInfo(vertexFormatType, mFeatureLevel); - - inputElements[inputElementCount].SemanticName = semanticName; - inputElements[inputElementCount].SemanticIndex = sortedSemanticIndices[i]; - inputElements[inputElementCount].Format = vertexFormatInfo.nativeFormat; - inputElements[inputElementCount].InputSlot = i; - inputElements[inputElementCount].AlignedByteOffset = 0; - inputElements[inputElementCount].InputSlotClass = inputClass; - inputElements[inputElementCount].InstanceDataStepRate = instancedPointSpritesActive ? 1 : sortedAttributes[i]->divisor; - - if (inputClass == D3D11_INPUT_PER_VERTEX_DATA && firstIndexedElement == gl::MAX_VERTEX_ATTRIBS) + Optional firstNonInstancedIndex = + FindFirstNonInstanced(mSortedAttributes, unsortedAttributes.size()); + if (firstNonInstancedIndex.valid()) { - firstIndexedElement = inputElementCount; - } - else if (inputClass == D3D11_INPUT_PER_INSTANCE_DATA && firstInstancedElement == gl::MAX_VERTEX_ATTRIBS) - { - firstInstancedElement = inputElementCount; - } - - // Record the type of the associated vertex shader vector in our key - // This will prevent mismatched vertex shaders from using the same input layout - GLenum glslElementType = GetGLSLAttributeType( - shaderAttributes, semanticToLocation[sortedSemanticIndices[i]]); - - layout.addAttributeData(glslElementType, - sortedSemanticIndices[i], - vertexFormatType, - sortedAttributes[i]->divisor); - - inputElementCount++; - nextAvailableInputSlot = i + 1; - } - } - - // Instanced PointSprite emulation requires additional entries in the - // inputlayout to support the vertices that make up the pointsprite quad. - // We do this even if mode != GL_POINTS, since the shader signature has these inputs, and the input layout must match the shader - if (programUsesInstancedPointSprites) - { - inputElements[inputElementCount].SemanticName = "SPRITEPOSITION"; - inputElements[inputElementCount].SemanticIndex = 0; - inputElements[inputElementCount].Format = DXGI_FORMAT_R32G32B32_FLOAT; - inputElements[inputElementCount].InputSlot = nextAvailableInputSlot; - inputElements[inputElementCount].AlignedByteOffset = 0; - inputElements[inputElementCount].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; - inputElements[inputElementCount].InstanceDataStepRate = 0; - - // The new elements are D3D11_INPUT_PER_VERTEX_DATA data so the indexed element - // tracking must be applied. This ensures that the instancing specific - // buffer swapping logic continues to work. - if (firstIndexedElement == gl::MAX_VERTEX_ATTRIBS) - { - firstIndexedElement = inputElementCount; - } - - inputElementCount++; - - inputElements[inputElementCount].SemanticName = "SPRITETEXCOORD"; - inputElements[inputElementCount].SemanticIndex = 0; - inputElements[inputElementCount].Format = DXGI_FORMAT_R32G32_FLOAT; - inputElements[inputElementCount].InputSlot = nextAvailableInputSlot; - inputElements[inputElementCount].AlignedByteOffset = sizeof(float) * 3; - inputElements[inputElementCount].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; - inputElements[inputElementCount].InstanceDataStepRate = 0; - - inputElementCount++; - } - - // On 9_3, we must ensure that slot 0 contains non-instanced data. - // If slot 0 currently contains instanced data then we swap it with a non-instanced element. - // Note that instancing is only available on 9_3 via ANGLE_instanced_arrays, since 9_3 doesn't support OpenGL ES 3.0. - // As per the spec for ANGLE_instanced_arrays, not all attributes can be instanced simultaneously, so a non-instanced element must exist. - ASSERT(!(mFeatureLevel <= D3D_FEATURE_LEVEL_9_3 && firstIndexedElement == gl::MAX_VERTEX_ATTRIBS)); - bool moveFirstIndexedIntoSlotZero = mFeatureLevel <= D3D_FEATURE_LEVEL_9_3 && firstInstancedElement == 0 && firstIndexedElement != gl::MAX_VERTEX_ATTRIBS; - - if (moveFirstIndexedIntoSlotZero) - { - inputElements[firstInstancedElement].InputSlot = inputElements[firstIndexedElement].InputSlot; - inputElements[firstIndexedElement].InputSlot = 0; - - // Instanced PointSprite emulation uses multiple layout entries across a single vertex buffer. - // If an index swap is performed, we need to ensure that all elements get the proper InputSlot. - if (programUsesInstancedPointSprites) - { - inputElements[firstIndexedElement + 1].InputSlot = 0; - } - } - - if (programUsesInstancedPointSprites) - { - layout.flags |= PackedAttributeLayout::FLAG_USES_INSTANCED_SPRITES; - } - - if (moveFirstIndexedIntoSlotZero) - { - layout.flags |= PackedAttributeLayout::FLAG_MOVE_FIRST_INDEXED; - } - - if (instancedPointSpritesActive) - { - layout.flags |= PackedAttributeLayout::FLAG_INSTANCED_SPRITES_ACTIVE; - } - - ID3D11InputLayout *inputLayout = nullptr; - - auto layoutMapIt = mLayoutMap.find(layout); - if (layoutMapIt != mLayoutMap.end()) - { - inputLayout = layoutMapIt->second; - } - else - { - const gl::InputLayout &shaderInputLayout = - GetInputLayout(sortedAttributes, unsortedAttributes.size()); - - ShaderExecutableD3D *shader = nullptr; - gl::Error error = programD3D->getVertexExecutableForInputLayout(shaderInputLayout, &shader, nullptr); - if (error.isError()) - { - return error; - } - - ShaderExecutableD3D *shader11 = GetAs(shader); - - D3D11_INPUT_ELEMENT_DESC descs[gl::MAX_VERTEX_ATTRIBS]; - for (unsigned int j = 0; j < inputElementCount; ++j) - { - descs[j] = inputElements[j]; - } - - HRESULT result = mDevice->CreateInputLayout(descs, inputElementCount, shader11->getFunction(), shader11->getLength(), &inputLayout); - if (FAILED(result)) - { - return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal input layout, HRESULT: 0x%08x", result); - } - - if (mLayoutMap.size() >= mCacheSize) - { - TRACE("Overflowed the limit of %u input layouts, purging half the cache.", mCacheSize); - - // Randomly release every second element - auto it = mLayoutMap.begin(); - while (it != mLayoutMap.end()) - { - it++; - if (it != mLayoutMap.end()) - { - // Calling std::map::erase invalidates the current iterator, so make a copy. - auto eraseIt = it++; - SafeRelease(eraseIt->second); - mLayoutMap.erase(eraseIt); - } + size_t index = firstNonInstancedIndex.value(); + std::swap(mSortedAttributes[0], mSortedAttributes[index]); + std::swap(sortedSemanticIndices[0], sortedSemanticIndices[index]); } } - - mLayoutMap[layout] = inputLayout; } - if (inputLayout != mCurrentIL) + gl::Error error = updateInputLayout(program, mode, mSortedAttributes, sortedSemanticIndices, + unsortedAttributes.size(), numIndicesPerInstance); + if (error.isError()) { - mDeviceContext->IASetInputLayout(inputLayout); - mCurrentIL = inputLayout; + return error; } bool dirtyBuffers = false; - unsigned int minDiff = gl::MAX_VERTEX_ATTRIBS; - unsigned int maxDiff = 0; - unsigned int nextAvailableIndex = 0; + size_t minDiff = gl::MAX_VERTEX_ATTRIBS; + size_t maxDiff = 0; - for (unsigned int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++) + // Note that if we use instance emulation, we reserve the first buffer slot. + size_t reservedBuffers = GetReservedBufferCount(programUsesInstancedPointSprites); + + for (size_t attribIndex = 0; attribIndex < (gl::MAX_VERTEX_ATTRIBS - reservedBuffers); + ++attribIndex) { ID3D11Buffer *buffer = NULL; UINT vertexStride = 0; UINT vertexOffset = 0; - if (i < unsortedAttributes.size() && sortedAttributes[i]->active) + const auto &attrib = *mSortedAttributes[attribIndex]; + + if (attribIndex < unsortedAttributes.size() && attrib.active) { - VertexBuffer11 *vertexBuffer = GetAs(sortedAttributes[i]->vertexBuffer); - Buffer11 *bufferStorage = sortedAttributes[i]->storage ? GetAs(sortedAttributes[i]->storage) : NULL; + VertexBuffer11 *vertexBuffer = GetAs(attrib.vertexBuffer); + Buffer11 *bufferStorage = attrib.storage ? GetAs(attrib.storage) : nullptr; // If indexed pointsprite emulation is active, then we need to take a less efficent code path. // Emulated indexed pointsprite rendering requires that the vertex buffers match exactly to @@ -401,64 +263,58 @@ gl::Error InputLayoutCache::applyVertexBuffers(const std::vectorgetBuffer(); } - else if (indexedPointSpriteEmulationActive) + else if (instancedPointSpritesActive && (indexInfo != nullptr)) { - if (sourceInfo->srcBuffer != nullptr) + if (indexInfo->srcIndexData.srcBuffer != nullptr) { const uint8_t *bufferData = nullptr; - gl::Error error = sourceInfo->srcBuffer->getData(&bufferData); + error = indexInfo->srcIndexData.srcBuffer->getData(&bufferData); if (error.isError()) { return error; } ASSERT(bufferData != nullptr); - ptrdiff_t offset = reinterpret_cast(sourceInfo->srcIndices); - sourceInfo->srcBuffer = nullptr; - sourceInfo->srcIndices = bufferData + offset; + ptrdiff_t offset = + reinterpret_cast(indexInfo->srcIndexData.srcIndices); + indexInfo->srcIndexData.srcBuffer = nullptr; + indexInfo->srcIndexData.srcIndices = bufferData + offset; } - buffer = bufferStorage->getEmulatedIndexedBuffer(sourceInfo, sortedAttributes[i]); + buffer = bufferStorage->getEmulatedIndexedBuffer(&indexInfo->srcIndexData, &attrib); } else { buffer = bufferStorage->getBuffer(BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK); } - vertexStride = sortedAttributes[i]->stride; - vertexOffset = sortedAttributes[i]->offset; + vertexStride = attrib.stride; + vertexOffset = attrib.offset; } - if (buffer != mCurrentBuffers[i] || vertexStride != mCurrentVertexStrides[i] || - vertexOffset != mCurrentVertexOffsets[i]) + size_t bufferIndex = reservedBuffers + attribIndex; + + if (buffer != mCurrentBuffers[bufferIndex] || + vertexStride != mCurrentVertexStrides[bufferIndex] || + vertexOffset != mCurrentVertexOffsets[bufferIndex]) { dirtyBuffers = true; - minDiff = std::min(minDiff, i); - maxDiff = std::max(maxDiff, i); + minDiff = std::min(minDiff, bufferIndex); + maxDiff = std::max(maxDiff, bufferIndex); - mCurrentBuffers[i] = buffer; - mCurrentVertexStrides[i] = vertexStride; - mCurrentVertexOffsets[i] = vertexOffset; - } - - // If a non null ID3D11Buffer is being assigned to mCurrentBuffers, - // then the next available index needs to be tracked to ensure - // that any instanced pointsprite emulation buffers will be properly packed. - if (buffer) - { - nextAvailableIndex = i + 1; + mCurrentBuffers[bufferIndex] = buffer; + mCurrentVertexStrides[bufferIndex] = vertexStride; + mCurrentVertexOffsets[bufferIndex] = vertexOffset; } } - // Instanced PointSprite emulation requires two additional ID3D11Buffers. - // A vertex buffer needs to be created and added to the list of current buffers, - // strides and offsets collections. This buffer contains the vertices for a single - // PointSprite quad. - // An index buffer also needs to be created and applied because rendering instanced - // data on D3D11 FL9_3 requires DrawIndexedInstanced() to be used. - // Shaders that contain gl_PointSize and used without the GL_POINTS rendering mode - // require a vertex buffer because some drivers cannot handle missing vertex data - // and will TDR the system. + // Instanced PointSprite emulation requires two additional ID3D11Buffers. A vertex buffer needs + // to be created and added to the list of current buffers, strides and offsets collections. + // This buffer contains the vertices for a single PointSprite quad. + // An index buffer also needs to be created and applied because rendering instanced data on + // D3D11 FL9_3 requires DrawIndexedInstanced() to be used. Shaders that contain gl_PointSize and + // used without the GL_POINTS rendering mode require a vertex buffer because some drivers cannot + // handle missing vertex data and will TDR the system. if (programUsesInstancedPointSprites) { HRESULT result = S_OK; @@ -493,16 +349,16 @@ gl::Error InputLayoutCache::applyVertexBuffers(const std::vector(0)); if (!mPointSpriteIndexBuffer) { @@ -531,34 +387,263 @@ gl::Error InputLayoutCache::applyVertexBuffers(const std::vectorIASetIndexBuffer(mPointSpriteIndexBuffer, DXGI_FORMAT_R16_UINT, 0); } } - if (moveFirstIndexedIntoSlotZero) - { - // In this case, we swapped the slots of the first instanced element and the first indexed element, to ensure - // that the first slot contains non-instanced data (required by Feature Level 9_3). - // We must also swap the corresponding buffers sent to IASetVertexBuffers so that the correct data is sent to each slot. - std::swap(mCurrentBuffers[firstIndexedElement], mCurrentBuffers[firstInstancedElement]); - std::swap(mCurrentVertexStrides[firstIndexedElement], mCurrentVertexStrides[firstInstancedElement]); - std::swap(mCurrentVertexOffsets[firstIndexedElement], mCurrentVertexOffsets[firstInstancedElement]); - } - if (dirtyBuffers) { ASSERT(minDiff <= maxDiff && maxDiff < gl::MAX_VERTEX_ATTRIBS); - mDeviceContext->IASetVertexBuffers(minDiff, maxDiff - minDiff + 1, mCurrentBuffers + minDiff, - mCurrentVertexStrides + minDiff, mCurrentVertexOffsets + minDiff); + mDeviceContext->IASetVertexBuffers( + static_cast(minDiff), static_cast(maxDiff - minDiff + 1), + mCurrentBuffers + minDiff, mCurrentVertexStrides + minDiff, + mCurrentVertexOffsets + minDiff); } return gl::Error(GL_NO_ERROR); } +gl::Error InputLayoutCache::updateVertexOffsetsForPointSpritesEmulation(GLsizei emulatedInstanceId) +{ + size_t reservedBuffers = GetReservedBufferCount(true); + for (size_t attribIndex = 0; attribIndex < mUnsortedAttributesCount; ++attribIndex) + { + const auto &attrib = *mSortedAttributes[attribIndex]; + size_t bufferIndex = reservedBuffers + attribIndex; + + if (attrib.active && attrib.divisor > 0) + { + mCurrentVertexOffsets[bufferIndex] = + attrib.offset + (attrib.stride * (emulatedInstanceId / attrib.divisor)); + } + } + + mDeviceContext->IASetVertexBuffers(0, gl::MAX_VERTEX_ATTRIBS, mCurrentBuffers, + mCurrentVertexStrides, mCurrentVertexOffsets); + + return gl::Error(GL_NO_ERROR); } + +gl::Error InputLayoutCache::updateInputLayout(gl::Program *program, + GLenum mode, + const SortedAttribArray &sortedAttributes, + const SortedIndexArray &sortedSemanticIndices, + size_t attribCount, + GLsizei numIndicesPerInstance) +{ + const std::vector &shaderAttributes = program->getAttributes(); + PackedAttributeLayout layout; + + ProgramD3D *programD3D = GetImplAs(program); + bool programUsesInstancedPointSprites = + programD3D->usesPointSize() && programD3D->usesInstancedPointSpriteEmulation(); + bool instancedPointSpritesActive = programUsesInstancedPointSprites && (mode == GL_POINTS); + + if (programUsesInstancedPointSprites) + { + layout.flags |= PackedAttributeLayout::FLAG_USES_INSTANCED_SPRITES; + } + + if (instancedPointSpritesActive) + { + layout.flags |= PackedAttributeLayout::FLAG_INSTANCED_SPRITES_ACTIVE; + } + + if (numIndicesPerInstance > 0) + { + layout.flags |= PackedAttributeLayout::FLAG_INSTANCED_RENDERING_ACTIVE; + } + + const auto &semanticToLocation = programD3D->getAttributesByLayout(); + + for (size_t attribIndex = 0; attribIndex < attribCount; ++attribIndex) + { + const auto &attrib = *sortedAttributes[attribIndex]; + int sortedIndex = sortedSemanticIndices[attribIndex]; + + if (!attrib.active) + continue; + + gl::VertexFormatType vertexFormatType = + gl::GetVertexFormatType(*attrib.attribute, attrib.currentValueType); + + // Record the type of the associated vertex shader vector in our key + // This will prevent mismatched vertex shaders from using the same input layout + GLenum glslElementType = + GetGLSLAttributeType(shaderAttributes, semanticToLocation[sortedIndex]); + + layout.addAttributeData(glslElementType, sortedIndex, vertexFormatType, attrib.divisor); + } + + ID3D11InputLayout *inputLayout = nullptr; + if (layout.numAttributes > 0 || layout.flags != 0) + { + auto layoutMapIt = mLayoutMap.find(layout); + if (layoutMapIt != mLayoutMap.end()) + { + inputLayout = layoutMapIt->second; + } + else + { + gl::Error error = + createInputLayout(sortedAttributes, sortedSemanticIndices, attribCount, mode, + program, numIndicesPerInstance, &inputLayout); + if (error.isError()) + { + return error; + } + if (mLayoutMap.size() >= mCacheSize) + { + TRACE("Overflowed the limit of %u input layouts, purging half the cache.", + mCacheSize); + + // Randomly release every second element + auto it = mLayoutMap.begin(); + while (it != mLayoutMap.end()) + { + it++; + if (it != mLayoutMap.end()) + { + // c++11 erase allows us to easily delete the current iterator. + SafeRelease(it->second); + it = mLayoutMap.erase(it); + } + } + } + + mLayoutMap[layout] = inputLayout; + } + } + + if (inputLayout != mCurrentIL) + { + mDeviceContext->IASetInputLayout(inputLayout); + mCurrentIL = inputLayout; + } + + return gl::Error(GL_NO_ERROR); +} + +gl::Error InputLayoutCache::createInputLayout(const SortedAttribArray &sortedAttributes, + const SortedIndexArray &sortedSemanticIndices, + size_t attribCount, + GLenum mode, + gl::Program *program, + GLsizei numIndicesPerInstance, + ID3D11InputLayout **inputLayoutOut) +{ + ProgramD3D *programD3D = GetImplAs(program); + + bool programUsesInstancedPointSprites = + programD3D->usesPointSize() && programD3D->usesInstancedPointSpriteEmulation(); + + unsigned int inputElementCount = 0; + std::array inputElements; + + for (size_t attribIndex = 0; attribIndex < attribCount; ++attribIndex) + { + const auto &attrib = *sortedAttributes[attribIndex]; + const int sortedIndex = sortedSemanticIndices[attribIndex]; + + if (!attrib.active) + continue; + + D3D11_INPUT_CLASSIFICATION inputClass = + attrib.divisor > 0 ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA; + + const auto &vertexFormatType = + gl::GetVertexFormatType(*attrib.attribute, attrib.currentValueType); + const auto &vertexFormatInfo = d3d11::GetVertexFormatInfo(vertexFormatType, mFeatureLevel); + + auto *inputElement = &inputElements[inputElementCount]; + + inputElement->SemanticName = "TEXCOORD"; + inputElement->SemanticIndex = sortedIndex; + inputElement->Format = vertexFormatInfo.nativeFormat; + inputElement->InputSlot = static_cast(attribIndex); + inputElement->AlignedByteOffset = 0; + inputElement->InputSlotClass = inputClass; + inputElement->InstanceDataStepRate = attrib.divisor; + + inputElementCount++; + } + + // Instanced PointSprite emulation requires additional entries in the + // inputlayout to support the vertices that make up the pointsprite quad. + // We do this even if mode != GL_POINTS, since the shader signature has these inputs, and the + // input layout must match the shader + if (programUsesInstancedPointSprites) + { + // On 9_3, we must ensure that slot 0 contains non-instanced data. + // If slot 0 currently contains instanced data then we swap it with a non-instanced element. + // Note that instancing is only available on 9_3 via ANGLE_instanced_arrays, since 9_3 + // doesn't support OpenGL ES 3.0. + // As per the spec for ANGLE_instanced_arrays, not all attributes can be instanced + // simultaneously, so a non-instanced element must exist. + for (size_t elementIndex = 0; elementIndex < inputElementCount; ++elementIndex) + { + if (sortedAttributes[elementIndex]->active) + { + // If rendering points and instanced pointsprite emulation is being used, the + // inputClass is required to be configured as per instance data + if (mode == GL_POINTS) + { + inputElements[elementIndex].InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA; + inputElements[elementIndex].InstanceDataStepRate = 1; + if (numIndicesPerInstance > 0 && sortedAttributes[elementIndex]->divisor > 0) + { + inputElements[elementIndex].InstanceDataStepRate = numIndicesPerInstance; + } + } + inputElements[elementIndex].InputSlot++; + } + } + + inputElements[inputElementCount].SemanticName = "SPRITEPOSITION"; + inputElements[inputElementCount].SemanticIndex = 0; + inputElements[inputElementCount].Format = DXGI_FORMAT_R32G32B32_FLOAT; + inputElements[inputElementCount].InputSlot = 0; + inputElements[inputElementCount].AlignedByteOffset = 0; + inputElements[inputElementCount].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + inputElements[inputElementCount].InstanceDataStepRate = 0; + inputElementCount++; + + inputElements[inputElementCount].SemanticName = "SPRITETEXCOORD"; + inputElements[inputElementCount].SemanticIndex = 0; + inputElements[inputElementCount].Format = DXGI_FORMAT_R32G32_FLOAT; + inputElements[inputElementCount].InputSlot = 0; + inputElements[inputElementCount].AlignedByteOffset = sizeof(float) * 3; + inputElements[inputElementCount].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + inputElements[inputElementCount].InstanceDataStepRate = 0; + inputElementCount++; + } + + const gl::InputLayout &shaderInputLayout = GetInputLayout(sortedAttributes, attribCount); + + ShaderExecutableD3D *shader = nullptr; + gl::Error error = + programD3D->getVertexExecutableForInputLayout(shaderInputLayout, &shader, nullptr); + if (error.isError()) + { + return error; + } + + ShaderExecutableD3D *shader11 = GetAs(shader); + + HRESULT result = + mDevice->CreateInputLayout(inputElements.data(), inputElementCount, shader11->getFunction(), + shader11->getLength(), inputLayoutOut); + if (FAILED(result)) + { + return gl::Error(GL_OUT_OF_MEMORY, + "Failed to create internal input layout, HRESULT: 0x%08x", result); + } + + return gl::Error(GL_NO_ERROR); +} + +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.h index 4cc0ec9b68..e208ae3c64 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.h @@ -13,8 +13,9 @@ #include #include + +#include #include -#include #include "common/angleutils.h" #include "libANGLE/Constants.h" @@ -31,6 +32,10 @@ namespace rx struct TranslatedAttribute; struct TranslatedIndexData; struct SourceIndexData; +class ProgramD3D; + +using SortedAttribArray = std::array; +using SortedIndexArray = std::array; class InputLayoutCache : angle::NonCopyable { @@ -43,7 +48,12 @@ class InputLayoutCache : angle::NonCopyable void markDirty(); gl::Error applyVertexBuffers(const std::vector &attributes, - GLenum mode, gl::Program *program, SourceIndexData *sourceInfo); + GLenum mode, + gl::Program *program, + TranslatedIndexData *indexInfo, + GLsizei numIndicesPerInstance); + + gl::Error updateVertexOffsetsForPointSpritesEmulation(GLsizei emulatedInstanceId); // Useful for testing void setCacheSize(unsigned int cacheSize) { mCacheSize = cacheSize; } @@ -66,9 +76,9 @@ class InputLayoutCache : angle::NonCopyable enum Flags { - FLAG_USES_INSTANCED_SPRITES = 0x1, - FLAG_MOVE_FIRST_INDEXED = 0x2, - FLAG_INSTANCED_SPRITES_ACTIVE = 0x4, + FLAG_USES_INSTANCED_SPRITES = 0x1, + FLAG_INSTANCED_SPRITES_ACTIVE = 0x2, + FLAG_INSTANCED_RENDERING_ACTIVE = 0x4, }; size_t numAttributes; @@ -76,12 +86,28 @@ class InputLayoutCache : angle::NonCopyable uint32_t attributeData[gl::MAX_VERTEX_ATTRIBS]; }; + gl::Error updateInputLayout(gl::Program *program, + GLenum mode, + const SortedAttribArray &sortedAttributes, + const SortedIndexArray &sortedSemanticIndices, + size_t attribCount, + GLsizei numIndicesPerInstance); + gl::Error createInputLayout(const SortedAttribArray &sortedAttributes, + const SortedIndexArray &sortedSemanticIndices, + size_t attribCount, + GLenum mode, + gl::Program *program, + GLsizei numIndicesPerInstance, + ID3D11InputLayout **inputLayoutOut); + std::map mLayoutMap; ID3D11InputLayout *mCurrentIL; ID3D11Buffer *mCurrentBuffers[gl::MAX_VERTEX_ATTRIBS]; UINT mCurrentVertexStrides[gl::MAX_VERTEX_ATTRIBS]; UINT mCurrentVertexOffsets[gl::MAX_VERTEX_ATTRIBS]; + SortedAttribArray mSortedAttributes; + size_t mUnsortedAttributesCount; ID3D11Buffer *mPointSpriteVertexBuffer; ID3D11Buffer *mPointSpriteIndexBuffer; @@ -94,6 +120,6 @@ class InputLayoutCache : angle::NonCopyable D3D_FEATURE_LEVEL mFeatureLevel; }; -} +} // namespace rx #endif // LIBANGLE_RENDERER_D3D_D3D11_INPUTLAYOUTCACHE_H_ diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/NativeWindow.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/NativeWindow.h index e183e4a804..b49f8faec8 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/NativeWindow.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/NativeWindow.h @@ -46,14 +46,21 @@ typedef IDXGIFactory DXGIFactory; #endif #endif +typedef interface IDCompositionDevice IDCompositionDevice; +typedef interface IDCompositionTarget IDCompositionTarget; +typedef interface IDCompositionVisual IDCompositionVisual; + namespace rx { class NativeWindow { public: - explicit NativeWindow(EGLNativeWindowType window, const egl::Config *config); + explicit NativeWindow(EGLNativeWindowType window, + const egl::Config *config, + bool directComposition); + ~NativeWindow(); bool initialize(); bool getClientRect(LPRECT rect); bool isIconic(); @@ -67,11 +74,23 @@ class NativeWindow inline EGLNativeWindowType getNativeWindow() const { return mWindow; } + void commitChange(); + private: EGLNativeWindowType mWindow; -#if defined(ANGLE_ENABLE_WINDOWS_STORE) + bool mDirectComposition; + +#ifdef HAS_DIRECT_COMPOSITION + + IDCompositionDevice *mDevice; + IDCompositionTarget *mCompositionTarget; + IDCompositionVisual *mVisual; + +#endif // HAS_DIRECT_COMPOSITION + const egl::Config *mConfig; +#if defined(ANGLE_ENABLE_WINDOWS_STORE) std::shared_ptr mImpl; #endif diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/PixelTransfer11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/PixelTransfer11.cpp index bb238acea1..dfc521f14f 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/PixelTransfer11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/PixelTransfer11.cpp @@ -224,11 +224,12 @@ gl::Error PixelTransfer11::copyBufferToTexture(const gl::PixelUnpackState &unpac // Are we doing a 2D or 3D copy? ID3D11GeometryShader *geometryShader = ((destSize.depth > 1) ? mBufferToTextureGS : NULL); + auto stateManager = mRenderer->getStateManager(); deviceContext->VSSetShader(mBufferToTextureVS, NULL, 0); deviceContext->GSSetShader(geometryShader, NULL, 0); deviceContext->PSSetShader(pixelShader, NULL, 0); - mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, bufferSRV); + stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, bufferSRV); deviceContext->IASetInputLayout(NULL); deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST); @@ -261,7 +262,7 @@ gl::Error PixelTransfer11::copyBufferToTexture(const gl::PixelUnpackState &unpac deviceContext->Draw(numPixels, 0); // Unbind textures and render targets and vertex buffer - mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, NULL); + stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, NULL); deviceContext->VSSetConstantBuffers(0, 1, &nullBuffer); mRenderer->markAllStateDirty(); diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Query11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Query11.cpp index e0101902ef..c015ff4a45 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Query11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Query11.cpp @@ -21,38 +21,81 @@ Query11::Query11(Renderer11 *renderer, GLenum type) mResult(0), mQueryFinished(false), mRenderer(renderer), - mQuery(NULL) + mQuery(nullptr), + mTimestampBeginQuery(nullptr), + mTimestampEndQuery(nullptr) { } Query11::~Query11() { SafeRelease(mQuery); + SafeRelease(mTimestampBeginQuery); + SafeRelease(mTimestampEndQuery); } gl::Error Query11::begin() { - if (mQuery == NULL) + if (mQuery == nullptr) { D3D11_QUERY_DESC queryDesc; queryDesc.Query = gl_d3d11::ConvertQueryType(getType()); queryDesc.MiscFlags = 0; - HRESULT result = mRenderer->getDevice()->CreateQuery(&queryDesc, &mQuery); + ID3D11Device *device = mRenderer->getDevice(); + + HRESULT result = device->CreateQuery(&queryDesc, &mQuery); if (FAILED(result)) { return gl::Error(GL_OUT_OF_MEMORY, "Internal query creation failed, result: 0x%X.", result); } + + // If we are doing time elapsed we also need a query to actually query the timestamp + if (getType() == GL_TIME_ELAPSED_EXT) + { + D3D11_QUERY_DESC desc; + desc.Query = D3D11_QUERY_TIMESTAMP; + desc.MiscFlags = 0; + result = device->CreateQuery(&desc, &mTimestampBeginQuery); + if (FAILED(result)) + { + return gl::Error(GL_OUT_OF_MEMORY, "Internal query creation failed, result: 0x%X.", + result); + } + result = device->CreateQuery(&desc, &mTimestampEndQuery); + if (FAILED(result)) + { + return gl::Error(GL_OUT_OF_MEMORY, "Internal query creation failed, result: 0x%X.", + result); + } + } } - mRenderer->getDeviceContext()->Begin(mQuery); + ID3D11DeviceContext *context = mRenderer->getDeviceContext(); + + context->Begin(mQuery); + + // If we are doing time elapsed query the begin timestamp + if (getType() == GL_TIME_ELAPSED_EXT) + { + context->End(mTimestampBeginQuery); + } return gl::Error(GL_NO_ERROR); } gl::Error Query11::end() { ASSERT(mQuery); - mRenderer->getDeviceContext()->End(mQuery); + + ID3D11DeviceContext *context = mRenderer->getDeviceContext(); + + // If we are doing time elapsed query the end timestamp + if (getType() == GL_TIME_ELAPSED_EXT) + { + context->End(mTimestampEndQuery); + } + + context->End(mQuery); mQueryFinished = false; mResult = GL_FALSE; @@ -60,7 +103,17 @@ gl::Error Query11::end() return gl::Error(GL_NO_ERROR); } -gl::Error Query11::getResult(GLuint *params) +gl::Error Query11::queryCounter() +{ + // This doesn't do anything for D3D11 as we don't support timestamps + ASSERT(getType() == GL_TIMESTAMP_EXT); + mQueryFinished = true; + mResult = 0; + return gl::Error(GL_NO_ERROR); +} + +template +gl::Error Query11::getResultBase(T *params) { while (!mQueryFinished) { @@ -77,12 +130,32 @@ gl::Error Query11::getResult(GLuint *params) } ASSERT(mQueryFinished); - *params = mResult; + *params = static_cast(mResult); return gl::Error(GL_NO_ERROR); } -gl::Error Query11::isResultAvailable(GLuint *available) +gl::Error Query11::getResult(GLint *params) +{ + return getResultBase(params); +} + +gl::Error Query11::getResult(GLuint *params) +{ + return getResultBase(params); +} + +gl::Error Query11::getResult(GLint64 *params) +{ + return getResultBase(params); +} + +gl::Error Query11::getResult(GLuint64 *params) +{ + return getResultBase(params); +} + +gl::Error Query11::isResultAvailable(bool *available) { gl::Error error = testQuery(); if (error.isError()) @@ -90,7 +163,7 @@ gl::Error Query11::isResultAvailable(GLuint *available) return error; } - *available = (mQueryFinished ? GL_TRUE : GL_FALSE); + *available = mQueryFinished; return gl::Error(GL_NO_ERROR); } @@ -134,11 +207,77 @@ gl::Error Query11::testQuery() if (result == S_OK) { mQueryFinished = true; - mResult = static_cast(soStats.NumPrimitivesWritten); + mResult = static_cast(soStats.NumPrimitivesWritten); } } break; + case GL_TIME_ELAPSED_EXT: + { + D3D11_QUERY_DATA_TIMESTAMP_DISJOINT timeStats = {0}; + HRESULT result = context->GetData(mQuery, &timeStats, sizeof(timeStats), 0); + if (FAILED(result)) + { + return gl::Error(GL_OUT_OF_MEMORY, + "Failed to get the data of an internal query, result: 0x%X.", + result); + } + + if (result == S_OK) + { + UINT64 beginTime = 0; + HRESULT beginRes = + context->GetData(mTimestampBeginQuery, &beginTime, sizeof(UINT64), 0); + if (FAILED(beginRes)) + { + return gl::Error( + GL_OUT_OF_MEMORY, + "Failed to get the data of an internal query, result: 0x%X.", beginRes); + } + UINT64 endTime = 0; + HRESULT endRes = + context->GetData(mTimestampEndQuery, &endTime, sizeof(UINT64), 0); + if (FAILED(endRes)) + { + return gl::Error( + GL_OUT_OF_MEMORY, + "Failed to get the data of an internal query, result: 0x%X.", endRes); + } + + if (beginRes == S_OK && endRes == S_OK) + { + mQueryFinished = true; + if (timeStats.Disjoint) + { + mRenderer->setGPUDisjoint(); + } + static_assert(sizeof(UINT64) == sizeof(unsigned long long), + "D3D UINT64 isn't 64 bits"); + if (rx::IsUnsignedMultiplicationSafe(endTime - beginTime, 1000000000ull)) + { + mResult = ((endTime - beginTime) * 1000000000ull) / timeStats.Frequency; + } + else + { + mResult = std::numeric_limits::max() / timeStats.Frequency; + // If an overflow does somehow occur, there is no way the elapsed time + // is accurate, so we generate a disjoint event + mRenderer->setGPUDisjoint(); + } + } + } + } + break; + + case GL_TIMESTAMP_EXT: + { + // D3D11 doesn't support GL timestamp queries as D3D timestamps are not guaranteed + // to have any sort of continuity outside of a disjoint timestamp query block, which + // GL depends on + mResult = 0; + } + break; + default: UNREACHABLE(); break; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Query11.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Query11.h index bd53fed250..29a6e6f85d 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Query11.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Query11.h @@ -23,18 +23,27 @@ class Query11 : public QueryImpl virtual gl::Error begin(); virtual gl::Error end(); + virtual gl::Error queryCounter(); + virtual gl::Error getResult(GLint *params); virtual gl::Error getResult(GLuint *params); - virtual gl::Error isResultAvailable(GLuint *available); + virtual gl::Error getResult(GLint64 *params); + virtual gl::Error getResult(GLuint64 *params); + virtual gl::Error isResultAvailable(bool *available); private: gl::Error testQuery(); - GLuint mResult; + template + gl::Error getResultBase(T *params); + + GLuint64 mResult; bool mQueryFinished; Renderer11 *mRenderer; ID3D11Query *mQuery; + ID3D11Query *mTimestampBeginQuery; + ID3D11Query *mTimestampEndQuery; }; } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.cpp index 5678581031..2ee25cfb6c 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.cpp @@ -21,6 +21,7 @@ namespace rx { +using namespace gl_d3d11; template static void ClearStateMap(mapType &map) @@ -95,7 +96,7 @@ gl::Error RenderStateCache::getBlendState(const gl::Framebuffer *framebuffer, co bool mrt = false; const FramebufferD3D *framebufferD3D = GetImplAs(framebuffer); - const gl::AttachmentList &colorbuffers = framebufferD3D->getColorAttachmentsForRender(mRenderer->getWorkarounds()); + const gl::AttachmentList &colorbuffers = framebufferD3D->getColorAttachmentsForRender(); BlendStateKey key = {}; key.blendState = blendState; @@ -297,14 +298,31 @@ bool RenderStateCache::compareDepthStencilStates(const gl::DepthStencilState &a, return memcmp(&a, &b, sizeof(gl::DepthStencilState)) == 0; } -gl::Error RenderStateCache::getDepthStencilState(const gl::DepthStencilState &dsState, ID3D11DepthStencilState **outDSState) +gl::Error RenderStateCache::getDepthStencilState(const gl::DepthStencilState &originalState, + bool disableDepth, + bool disableStencil, + ID3D11DepthStencilState **outDSState) { if (!mDevice) { return gl::Error(GL_OUT_OF_MEMORY, "Internal error, RenderStateCache is not initialized."); } - DepthStencilStateMap::iterator keyIter = mDepthStencilStateCache.find(dsState); + gl::DepthStencilState glState = originalState; + if (disableDepth) + { + glState.depthTest = false; + glState.depthMask = false; + } + + if (disableStencil) + { + glState.stencilWritemask = 0; + glState.stencilBackWritemask = 0; + glState.stencilTest = false; + } + + auto keyIter = mDepthStencilStateCache.find(glState); if (keyIter != mDepthStencilStateCache.end()) { DepthStencilStateCounterPair &state = keyIter->second; @@ -312,53 +330,55 @@ gl::Error RenderStateCache::getDepthStencilState(const gl::DepthStencilState &ds *outDSState = state.first; return gl::Error(GL_NO_ERROR); } - else + + if (mDepthStencilStateCache.size() >= kMaxDepthStencilStates) { - if (mDepthStencilStateCache.size() >= kMaxDepthStencilStates) - { - TRACE("Overflowed the limit of %u depth stencil states, removing the least recently used " - "to make room.", kMaxDepthStencilStates); + TRACE( + "Overflowed the limit of %u depth stencil states, removing the least recently used " + "to make room.", + kMaxDepthStencilStates); - DepthStencilStateMap::iterator leastRecentlyUsed = mDepthStencilStateCache.begin(); - for (DepthStencilStateMap::iterator i = mDepthStencilStateCache.begin(); i != mDepthStencilStateCache.end(); i++) + auto leastRecentlyUsed = mDepthStencilStateCache.begin(); + for (auto i = mDepthStencilStateCache.begin(); i != mDepthStencilStateCache.end(); i++) + { + if (i->second.second < leastRecentlyUsed->second.second) { - if (i->second.second < leastRecentlyUsed->second.second) - { - leastRecentlyUsed = i; - } + leastRecentlyUsed = i; } - SafeRelease(leastRecentlyUsed->second.first); - mDepthStencilStateCache.erase(leastRecentlyUsed); } - - D3D11_DEPTH_STENCIL_DESC dsDesc = { 0 }; - dsDesc.DepthEnable = dsState.depthTest ? TRUE : FALSE; - dsDesc.DepthWriteMask = gl_d3d11::ConvertDepthMask(dsState.depthMask); - dsDesc.DepthFunc = gl_d3d11::ConvertComparison(dsState.depthFunc); - dsDesc.StencilEnable = dsState.stencilTest ? TRUE : FALSE; - dsDesc.StencilReadMask = gl_d3d11::ConvertStencilMask(dsState.stencilMask); - dsDesc.StencilWriteMask = gl_d3d11::ConvertStencilMask(dsState.stencilWritemask); - dsDesc.FrontFace.StencilFailOp = gl_d3d11::ConvertStencilOp(dsState.stencilFail); - dsDesc.FrontFace.StencilDepthFailOp = gl_d3d11::ConvertStencilOp(dsState.stencilPassDepthFail); - dsDesc.FrontFace.StencilPassOp = gl_d3d11::ConvertStencilOp(dsState.stencilPassDepthPass); - dsDesc.FrontFace.StencilFunc = gl_d3d11::ConvertComparison(dsState.stencilFunc); - dsDesc.BackFace.StencilFailOp = gl_d3d11::ConvertStencilOp(dsState.stencilBackFail); - dsDesc.BackFace.StencilDepthFailOp = gl_d3d11::ConvertStencilOp(dsState.stencilBackPassDepthFail); - dsDesc.BackFace.StencilPassOp = gl_d3d11::ConvertStencilOp(dsState.stencilBackPassDepthPass); - dsDesc.BackFace.StencilFunc = gl_d3d11::ConvertComparison(dsState.stencilBackFunc); - - ID3D11DepthStencilState *dx11DepthStencilState = NULL; - HRESULT result = mDevice->CreateDepthStencilState(&dsDesc, &dx11DepthStencilState); - if (FAILED(result) || !dx11DepthStencilState) - { - return gl::Error(GL_OUT_OF_MEMORY, "Unable to create a ID3D11DepthStencilState, HRESULT: 0x%X.", result); - } - - mDepthStencilStateCache.insert(std::make_pair(dsState, std::make_pair(dx11DepthStencilState, mCounter++))); - - *outDSState = dx11DepthStencilState; - return gl::Error(GL_NO_ERROR); + SafeRelease(leastRecentlyUsed->second.first); + mDepthStencilStateCache.erase(leastRecentlyUsed); } + + D3D11_DEPTH_STENCIL_DESC dsDesc = {0}; + dsDesc.DepthEnable = glState.depthTest ? TRUE : FALSE; + dsDesc.DepthWriteMask = ConvertDepthMask(glState.depthMask); + dsDesc.DepthFunc = ConvertComparison(glState.depthFunc); + dsDesc.StencilEnable = glState.stencilTest ? TRUE : FALSE; + dsDesc.StencilReadMask = ConvertStencilMask(glState.stencilMask); + dsDesc.StencilWriteMask = ConvertStencilMask(glState.stencilWritemask); + dsDesc.FrontFace.StencilFailOp = ConvertStencilOp(glState.stencilFail); + dsDesc.FrontFace.StencilDepthFailOp = ConvertStencilOp(glState.stencilPassDepthFail); + dsDesc.FrontFace.StencilPassOp = ConvertStencilOp(glState.stencilPassDepthPass); + dsDesc.FrontFace.StencilFunc = ConvertComparison(glState.stencilFunc); + dsDesc.BackFace.StencilFailOp = ConvertStencilOp(glState.stencilBackFail); + dsDesc.BackFace.StencilDepthFailOp = ConvertStencilOp(glState.stencilBackPassDepthFail); + dsDesc.BackFace.StencilPassOp = ConvertStencilOp(glState.stencilBackPassDepthPass); + dsDesc.BackFace.StencilFunc = ConvertComparison(glState.stencilBackFunc); + + ID3D11DepthStencilState *dx11DepthStencilState = NULL; + HRESULT result = mDevice->CreateDepthStencilState(&dsDesc, &dx11DepthStencilState); + if (FAILED(result) || !dx11DepthStencilState) + { + return gl::Error(GL_OUT_OF_MEMORY, + "Unable to create a ID3D11DepthStencilState, HRESULT: 0x%X.", result); + } + + mDepthStencilStateCache.insert( + std::make_pair(glState, std::make_pair(dx11DepthStencilState, mCounter++))); + + *outDSState = dx11DepthStencilState; + return gl::Error(GL_NO_ERROR); } std::size_t RenderStateCache::hashSamplerState(const gl::SamplerState &samplerState) diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.h index 0099b94a04..82cb13903c 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.h @@ -36,7 +36,10 @@ class RenderStateCache : angle::NonCopyable gl::Error getBlendState(const gl::Framebuffer *framebuffer, const gl::BlendState &blendState, ID3D11BlendState **outBlendState); gl::Error getRasterizerState(const gl::RasterizerState &rasterState, bool scissorEnabled, ID3D11RasterizerState **outRasterizerState); - gl::Error getDepthStencilState(const gl::DepthStencilState &dsState, ID3D11DepthStencilState **outDSState); + gl::Error getDepthStencilState(const gl::DepthStencilState &dsState, + bool disableDepth, + bool disableStencil, + ID3D11DepthStencilState **outDSState); gl::Error getSamplerState(const gl::SamplerState &samplerState, ID3D11SamplerState **outSamplerState); private: diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp index 9943753a76..a4ab8c1594 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp @@ -10,6 +10,7 @@ #include #include +#include #include "common/tls.h" #include "common/utilities.h" @@ -41,6 +42,7 @@ #include "libANGLE/renderer/d3d/d3d11/Trim11.h" #include "libANGLE/renderer/d3d/d3d11/VertexArray11.h" #include "libANGLE/renderer/d3d/d3d11/VertexBuffer11.h" +#include "libANGLE/renderer/d3d/CompilerD3D.h" #include "libANGLE/renderer/d3d/DeviceD3D.h" #include "libANGLE/renderer/d3d/FramebufferD3D.h" #include "libANGLE/renderer/d3d/IndexDataManager.h" @@ -85,73 +87,6 @@ enum MAX_TEXTURE_IMAGE_UNITS_VTF_SM4 = 16 }; -bool ImageIndexConflictsWithSRV(const gl::ImageIndex &index, D3D11_SHADER_RESOURCE_VIEW_DESC desc) -{ - unsigned mipLevel = index.mipIndex; - unsigned layerIndex = index.layerIndex; - GLenum type = index.type; - - switch (desc.ViewDimension) - { - case D3D11_SRV_DIMENSION_TEXTURE2D: - { - unsigned maxSrvMip = desc.Texture2D.MipLevels + desc.Texture2D.MostDetailedMip; - maxSrvMip = (desc.Texture2D.MipLevels == -1) ? INT_MAX : maxSrvMip; - - unsigned mipMin = index.mipIndex; - unsigned mipMax = (layerIndex == -1) ? INT_MAX : layerIndex; - - return type == GL_TEXTURE_2D && gl::RangeUI(mipMin, mipMax).intersects(gl::RangeUI(desc.Texture2D.MostDetailedMip, maxSrvMip)); - } - - case D3D11_SRV_DIMENSION_TEXTURE2DARRAY: - { - unsigned maxSrvMip = desc.Texture2DArray.MipLevels + desc.Texture2DArray.MostDetailedMip; - maxSrvMip = (desc.Texture2DArray.MipLevels == -1) ? INT_MAX : maxSrvMip; - - unsigned maxSlice = desc.Texture2DArray.FirstArraySlice + desc.Texture2DArray.ArraySize; - - // Cube maps can be mapped to Texture2DArray SRVs - return (type == GL_TEXTURE_2D_ARRAY || gl::IsCubeMapTextureTarget(type)) && - desc.Texture2DArray.MostDetailedMip <= mipLevel && mipLevel < maxSrvMip && - desc.Texture2DArray.FirstArraySlice <= layerIndex && layerIndex < maxSlice; - } - - case D3D11_SRV_DIMENSION_TEXTURECUBE: - { - unsigned maxSrvMip = desc.TextureCube.MipLevels + desc.TextureCube.MostDetailedMip; - maxSrvMip = (desc.TextureCube.MipLevels == -1) ? INT_MAX : maxSrvMip; - - return gl::IsCubeMapTextureTarget(type) && - desc.TextureCube.MostDetailedMip <= mipLevel && mipLevel < maxSrvMip; - } - - case D3D11_SRV_DIMENSION_TEXTURE3D: - { - unsigned maxSrvMip = desc.Texture3D.MipLevels + desc.Texture3D.MostDetailedMip; - maxSrvMip = (desc.Texture3D.MipLevels == -1) ? INT_MAX : maxSrvMip; - - return type == GL_TEXTURE_3D && - desc.Texture3D.MostDetailedMip <= mipLevel && mipLevel < maxSrvMip; - } - default: - // We only handle the cases corresponding to valid image indexes - UNIMPLEMENTED(); - } - - return false; -} - -// Does *not* increment the resource ref count!! -ID3D11Resource *GetViewResource(ID3D11View *view) -{ - ID3D11Resource *resource = NULL; - ASSERT(view); - view->GetResource(&resource); - resource->Release(); - return resource; -} - void CalculateConstantBufferParams(GLintptr offset, GLsizeiptr size, UINT *outFirstConstant, UINT *outNumConstants) { // The offset must be aligned to 256 bytes (should have been enforced by glBindBufferRange). @@ -428,49 +363,9 @@ void GetTriFanIndices(const GLvoid *indices, } // anonymous namespace -void Renderer11::SRVCache::update(size_t resourceIndex, ID3D11ShaderResourceView *srv) -{ - ASSERT(resourceIndex < mCurrentSRVs.size()); - SRVRecord *record = &mCurrentSRVs[resourceIndex]; - - record->srv = reinterpret_cast(srv); - if (srv) - { - record->resource = reinterpret_cast(GetViewResource(srv)); - srv->GetDesc(&record->desc); - mHighestUsedSRV = std::max(resourceIndex + 1, mHighestUsedSRV); - } - else - { - record->resource = 0; - - if (resourceIndex + 1 == mHighestUsedSRV) - { - do - { - --mHighestUsedSRV; - } - while (mHighestUsedSRV > 0 && - mCurrentSRVs[mHighestUsedSRV].srv == 0); - } - } -} - -void Renderer11::SRVCache::clear() -{ - if (mCurrentSRVs.empty()) - { - return; - } - - memset(&mCurrentSRVs[0], 0, sizeof(SRVRecord) * mCurrentSRVs.size()); - mHighestUsedSRV = 0; -} - Renderer11::Renderer11(egl::Display *display) : RendererD3D(display), mStateCache(this), - mCurStencilSize(0), mStateManager(this), mLastHistogramUpdateTime(ANGLEPlatformCurrent()->monotonicallyIncreasingTime()), mDebug(nullptr) @@ -500,6 +395,9 @@ Renderer11::Renderer11(egl::Display *display) mD3d11Module = NULL; mDxgiModule = NULL; + mDCompModule = NULL; + mCreatedWithDeviceEXT = false; + mEGLDevice = nullptr; mDevice = NULL; mDeviceContext = NULL; @@ -518,58 +416,78 @@ Renderer11::Renderer11(egl::Display *display) ZeroMemory(&mAdapterDescription, sizeof(mAdapterDescription)); - const auto &attributes = mDisplay->getAttributeMap(); - - EGLint requestedMajorVersion = attributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE); - EGLint requestedMinorVersion = attributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, EGL_DONT_CARE); - - if (requestedMajorVersion == EGL_DONT_CARE || requestedMajorVersion >= 11) + if (mDisplay->getPlatform() == EGL_PLATFORM_ANGLE_ANGLE) { - if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 0) + const auto &attributes = mDisplay->getAttributeMap(); + + EGLint requestedMajorVersion = + attributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE); + EGLint requestedMinorVersion = + attributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, EGL_DONT_CARE); + + if (requestedMajorVersion == EGL_DONT_CARE || requestedMajorVersion >= 11) { - mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_11_0); + if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 0) + { + mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_11_0); + } } - } - if (requestedMajorVersion == EGL_DONT_CARE || requestedMajorVersion >= 10) - { - if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 1) + if (requestedMajorVersion == EGL_DONT_CARE || requestedMajorVersion >= 10) { - mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_10_1); + if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 1) + { + mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_10_1); + } + if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 0) + { + mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_10_0); + } } - if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 0) + + if (requestedMajorVersion == 9 && requestedMinorVersion == 3) { - mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_10_0); + mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_9_3); } + + EGLint requestedDeviceType = attributes.get(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, + EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE); + switch (requestedDeviceType) + { + case EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE: + mRequestedDriverType = D3D_DRIVER_TYPE_HARDWARE; + break; + + case EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE: + mRequestedDriverType = D3D_DRIVER_TYPE_WARP; + break; + + case EGL_PLATFORM_ANGLE_DEVICE_TYPE_REFERENCE_ANGLE: + mRequestedDriverType = D3D_DRIVER_TYPE_REFERENCE; + break; + + case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE: + mRequestedDriverType = D3D_DRIVER_TYPE_NULL; + break; + + default: + UNREACHABLE(); + } + + const EGLenum presentPath = attributes.get(EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE, + EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE); + mPresentPathFastEnabled = (presentPath == EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE); } - - if (requestedMajorVersion == 9 && requestedMinorVersion == 3) + else if (display->getPlatform() == EGL_PLATFORM_DEVICE_EXT) { - mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_9_3); - } + mEGLDevice = GetImplAs(display->getDevice()); + ASSERT(mEGLDevice != nullptr); + mCreatedWithDeviceEXT = true; - EGLint requestedDeviceType = attributes.get(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, - EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE); - switch (requestedDeviceType) - { - case EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE: - mDriverType = D3D_DRIVER_TYPE_HARDWARE; - break; - - case EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE: - mDriverType = D3D_DRIVER_TYPE_WARP; - break; - - case EGL_PLATFORM_ANGLE_DEVICE_TYPE_REFERENCE_ANGLE: - mDriverType = D3D_DRIVER_TYPE_REFERENCE; - break; - - case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE: - mDriverType = D3D_DRIVER_TYPE_NULL; - break; - - default: - UNREACHABLE(); + // Also set EGL_PLATFORM_ANGLE_ANGLE variables, in case they're used elsewhere in ANGLE + // mAvailableFeatureLevels defaults to empty + mRequestedDriverType = D3D_DRIVER_TYPE_UNKNOWN; + mPresentPathFastEnabled = false; } initializeDebugAnnotator(); @@ -586,68 +504,12 @@ Renderer11::~Renderer11() egl::Error Renderer11::initialize() { -#if !defined(ANGLE_ENABLE_WINDOWS_STORE) - PFN_D3D11_CREATE_DEVICE D3D11CreateDevice = nullptr; - { - SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.Renderer11InitializeDLLsMS"); - TRACE_EVENT0("gpu.angle", "Renderer11::initialize (Load DLLs)"); - mDxgiModule = LoadLibrary(TEXT("dxgi.dll")); - mD3d11Module = LoadLibrary(TEXT("d3d11.dll")); - - if (mD3d11Module == nullptr || mDxgiModule == nullptr) - { - return egl::Error(EGL_NOT_INITIALIZED, - D3D11_INIT_MISSING_DEP, - "Could not load D3D11 or DXGI library."); - } - - // create the D3D11 device - ASSERT(mDevice == nullptr); - D3D11CreateDevice = reinterpret_cast(GetProcAddress(mD3d11Module, "D3D11CreateDevice")); - - if (D3D11CreateDevice == nullptr) - { - return egl::Error(EGL_NOT_INITIALIZED, - D3D11_INIT_MISSING_DEP, - "Could not retrieve D3D11CreateDevice address."); - } - } -#endif - HRESULT result = S_OK; -#ifdef _DEBUG + + egl::Error error = initializeD3DDevice(); + if (error.isError()) { - TRACE_EVENT0("gpu.angle", "D3D11CreateDevice (Debug)"); - result = D3D11CreateDevice( - NULL, mDriverType, NULL, D3D11_CREATE_DEVICE_DEBUG, mAvailableFeatureLevels.data(), - static_cast(mAvailableFeatureLevels.size()), D3D11_SDK_VERSION, &mDevice, - &(mRenderer11DeviceCaps.featureLevel), &mDeviceContext); - } - - if (!mDevice || FAILED(result)) - { - ERR("Failed creating Debug D3D11 device - falling back to release runtime.\n"); - } - - if (!mDevice || FAILED(result)) -#endif - { - SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.D3D11CreateDeviceMS"); - TRACE_EVENT0("gpu.angle", "D3D11CreateDevice"); - - result = D3D11CreateDevice(NULL, mDriverType, NULL, 0, mAvailableFeatureLevels.data(), - static_cast(mAvailableFeatureLevels.size()), - D3D11_SDK_VERSION, &mDevice, - &(mRenderer11DeviceCaps.featureLevel), &mDeviceContext); - - // Cleanup done by destructor - if (!mDevice || FAILED(result)) - { - ANGLE_HISTOGRAM_SPARSE_SLOWLY("GPU.ANGLE.D3D11CreateDeviceError", static_cast(result)); - return egl::Error(EGL_NOT_INITIALIZED, - D3D11_INIT_CREATEDEVICE_ERROR, - "Could not create D3D11 device."); - } + return error; } #if !defined(ANGLE_ENABLE_WINDOWS_STORE) @@ -796,6 +658,110 @@ egl::Error Renderer11::initialize() return egl::Error(EGL_SUCCESS); } +egl::Error Renderer11::initializeD3DDevice() +{ + HRESULT result = S_OK; + + if (!mCreatedWithDeviceEXT) + { +#if !defined(ANGLE_ENABLE_WINDOWS_STORE) + PFN_D3D11_CREATE_DEVICE D3D11CreateDevice = nullptr; + { + SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.Renderer11InitializeDLLsMS"); + TRACE_EVENT0("gpu.angle", "Renderer11::initialize (Load DLLs)"); + mDxgiModule = LoadLibrary(TEXT("dxgi.dll")); + mD3d11Module = LoadLibrary(TEXT("d3d11.dll")); + mDCompModule = LoadLibrary(TEXT("dcomp.dll")); + + if (mD3d11Module == nullptr || mDxgiModule == nullptr) + { + return egl::Error(EGL_NOT_INITIALIZED, D3D11_INIT_MISSING_DEP, + "Could not load D3D11 or DXGI library."); + } + + // create the D3D11 device + ASSERT(mDevice == nullptr); + D3D11CreateDevice = reinterpret_cast( + GetProcAddress(mD3d11Module, "D3D11CreateDevice")); + + if (D3D11CreateDevice == nullptr) + { + return egl::Error(EGL_NOT_INITIALIZED, D3D11_INIT_MISSING_DEP, + "Could not retrieve D3D11CreateDevice address."); + } + } +#endif + +#ifdef _DEBUG + { + TRACE_EVENT0("gpu.angle", "D3D11CreateDevice (Debug)"); + result = D3D11CreateDevice(nullptr, mRequestedDriverType, nullptr, + D3D11_CREATE_DEVICE_DEBUG, mAvailableFeatureLevels.data(), + static_cast(mAvailableFeatureLevels.size()), + D3D11_SDK_VERSION, &mDevice, + &(mRenderer11DeviceCaps.featureLevel), &mDeviceContext); + } + + if (!mDevice || FAILED(result)) + { + ERR("Failed creating Debug D3D11 device - falling back to release runtime.\n"); + } + + if (!mDevice || FAILED(result)) +#endif + { + SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.D3D11CreateDeviceMS"); + TRACE_EVENT0("gpu.angle", "D3D11CreateDevice"); + + result = D3D11CreateDevice( + nullptr, mRequestedDriverType, nullptr, 0, mAvailableFeatureLevels.data(), + static_cast(mAvailableFeatureLevels.size()), D3D11_SDK_VERSION, + &mDevice, &(mRenderer11DeviceCaps.featureLevel), &mDeviceContext); + + // Cleanup done by destructor + if (!mDevice || FAILED(result)) + { + ANGLE_HISTOGRAM_SPARSE_SLOWLY("GPU.ANGLE.D3D11CreateDeviceError", + static_cast(result)); + return egl::Error(EGL_NOT_INITIALIZED, D3D11_INIT_CREATEDEVICE_ERROR, + "Could not create D3D11 device."); + } + } + } + else + { + // We should use the inputted D3D11 device instead + void *device = nullptr; + egl::Error error = mEGLDevice->getDevice(&device); + if (error.isError()) + { + return error; + } + + ID3D11Device *d3dDevice = reinterpret_cast(device); + if (FAILED(d3dDevice->GetDeviceRemovedReason())) + { + return egl::Error(EGL_NOT_INITIALIZED, "Inputted D3D11 device has been lost."); + } + + if (d3dDevice->GetFeatureLevel() < D3D_FEATURE_LEVEL_9_3) + { + return egl::Error(EGL_NOT_INITIALIZED, + "Inputted D3D11 device must be Feature Level 9_3 or greater."); + } + + // The Renderer11 adds a ref to the inputted D3D11 device, like D3D11CreateDevice does. + mDevice = d3dDevice; + mDevice->AddRef(); + mDevice->GetImmediateContext(&mDeviceContext); + mRenderer11DeviceCaps.featureLevel = mDevice->GetFeatureLevel(); + } + + d3d11::SetDebugName(mDeviceContext, "DeviceContext"); + + return egl::Error(EGL_SUCCESS); +} + // do any one-time device initialization // NOTE: this is also needed after a device lost/reset // to reset the scene status and ensure the default states are reset. @@ -836,14 +802,15 @@ void Renderer11::initializeDevice() const gl::Caps &rendererCaps = getRendererCaps(); + mStateManager.initialize(rendererCaps); + mForceSetVertexSamplerStates.resize(rendererCaps.maxVertexTextureImageUnits); mCurVertexSamplerStates.resize(rendererCaps.maxVertexTextureImageUnits); mForceSetPixelSamplerStates.resize(rendererCaps.maxTextureImageUnits); mCurPixelSamplerStates.resize(rendererCaps.maxTextureImageUnits); - mCurVertexSRVs.initialize(rendererCaps.maxVertexTextureImageUnits); - mCurPixelSRVs.initialize(rendererCaps.maxTextureImageUnits); + mStateManager.initialize(rendererCaps); markAllStateDirty(); @@ -861,9 +828,6 @@ void Renderer11::initializeDevice() angleFeatureLevel = ANGLE_FEATURE_LEVEL_11_1; } - // Initialize cached NULL SRV block - mNullSRVs.resize(getRendererCaps().maxTextureImageUnits, nullptr); - ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.D3D11FeatureLevel", angleFeatureLevel, NUM_ANGLE_FEATURE_LEVELS); @@ -912,14 +876,24 @@ void Renderer11::populateRenderer11DeviceCaps() egl::ConfigSet Renderer11::generateConfigs() const { - static const GLenum colorBufferFormats[] = { - // 32-bit supported formats - GL_BGRA8_EXT, GL_RGBA8_OES, - // 24-bit supported formats - GL_RGB8_OES, + std::vector colorBufferFormats; + + // 32-bit supported formats + colorBufferFormats.push_back(GL_BGRA8_EXT); + colorBufferFormats.push_back(GL_RGBA8_OES); + + // 24-bit supported formats + colorBufferFormats.push_back(GL_RGB8_OES); + + if (!mPresentPathFastEnabled) + { // 16-bit supported formats - GL_RGBA4, GL_RGB5_A1, GL_RGB565, - }; + // These aren't valid D3D11 swapchain formats, so don't expose them as configs + // if present path fast is active + colorBufferFormats.push_back(GL_RGBA4); + colorBufferFormats.push_back(GL_RGB5_A1); + colorBufferFormats.push_back(GL_RGB565); + } static const GLenum depthStencilBufferFormats[] = { @@ -931,64 +905,87 @@ egl::ConfigSet Renderer11::generateConfigs() const const gl::Caps &rendererCaps = getRendererCaps(); const gl::TextureCapsMap &rendererTextureCaps = getRendererTextureCaps(); + const EGLint optimalSurfaceOrientation = + mPresentPathFastEnabled ? 0 : EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE; + egl::ConfigSet configs; - for (size_t formatIndex = 0; formatIndex < ArraySize(colorBufferFormats); formatIndex++) + for (GLenum colorBufferInternalFormat : colorBufferFormats) { - GLenum colorBufferInternalFormat = colorBufferFormats[formatIndex]; const gl::TextureCaps &colorBufferFormatCaps = rendererTextureCaps.get(colorBufferInternalFormat); - if (colorBufferFormatCaps.renderable) + if (!colorBufferFormatCaps.renderable) { - for (size_t depthStencilIndex = 0; depthStencilIndex < ArraySize(depthStencilBufferFormats); depthStencilIndex++) + continue; + } + + for (GLenum depthStencilBufferInternalFormat : depthStencilBufferFormats) + { + const gl::TextureCaps &depthStencilBufferFormatCaps = + rendererTextureCaps.get(depthStencilBufferInternalFormat); + if (!depthStencilBufferFormatCaps.renderable && + depthStencilBufferInternalFormat != GL_NONE) { - GLenum depthStencilBufferInternalFormat = depthStencilBufferFormats[depthStencilIndex]; - const gl::TextureCaps &depthStencilBufferFormatCaps = rendererTextureCaps.get(depthStencilBufferInternalFormat); - if (depthStencilBufferFormatCaps.renderable || depthStencilBufferInternalFormat == GL_NONE) - { - const gl::InternalFormat &colorBufferFormatInfo = gl::GetInternalFormatInfo(colorBufferInternalFormat); - const gl::InternalFormat &depthStencilBufferFormatInfo = gl::GetInternalFormatInfo(depthStencilBufferInternalFormat); - - egl::Config config; - config.renderTargetFormat = colorBufferInternalFormat; - config.depthStencilFormat = depthStencilBufferInternalFormat; - config.bufferSize = colorBufferFormatInfo.pixelBytes * 8; - config.redSize = colorBufferFormatInfo.redBits; - config.greenSize = colorBufferFormatInfo.greenBits; - config.blueSize = colorBufferFormatInfo.blueBits; - config.luminanceSize = colorBufferFormatInfo.luminanceBits; - config.alphaSize = colorBufferFormatInfo.alphaBits; - config.alphaMaskSize = 0; - config.bindToTextureRGB = (colorBufferFormatInfo.format == GL_RGB); - config.bindToTextureRGBA = (colorBufferFormatInfo.format == GL_RGBA || colorBufferFormatInfo.format == GL_BGRA_EXT); - config.colorBufferType = EGL_RGB_BUFFER; - config.configCaveat = EGL_NONE; - config.configID = static_cast(configs.size() + 1); - // Can only support a conformant ES2 with feature level greater than 10.0. - config.conformant = (mRenderer11DeviceCaps.featureLevel >= D3D_FEATURE_LEVEL_10_0) ? (EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT_KHR) : 0; - config.depthSize = depthStencilBufferFormatInfo.depthBits; - config.level = 0; - config.matchNativePixmap = EGL_NONE; - config.maxPBufferWidth = rendererCaps.max2DTextureSize; - config.maxPBufferHeight = rendererCaps.max2DTextureSize; - config.maxPBufferPixels = rendererCaps.max2DTextureSize * rendererCaps.max2DTextureSize; - config.maxSwapInterval = 4; - config.minSwapInterval = 0; - config.nativeRenderable = EGL_FALSE; - config.nativeVisualID = 0; - config.nativeVisualType = EGL_NONE; - // Can't support ES3 at all without feature level 10.0 - config.renderableType = EGL_OPENGL_ES2_BIT | ((mRenderer11DeviceCaps.featureLevel >= D3D_FEATURE_LEVEL_10_0) ? EGL_OPENGL_ES3_BIT_KHR : 0); - config.sampleBuffers = 0; // FIXME: enumerate multi-sampling - config.samples = 0; - config.stencilSize = depthStencilBufferFormatInfo.stencilBits; - config.surfaceType = EGL_PBUFFER_BIT | EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT; - config.transparentType = EGL_NONE; - config.transparentRedValue = 0; - config.transparentGreenValue = 0; - config.transparentBlueValue = 0; - - configs.add(config); - } + continue; } + + const gl::InternalFormat &colorBufferFormatInfo = + gl::GetInternalFormatInfo(colorBufferInternalFormat); + const gl::InternalFormat &depthStencilBufferFormatInfo = + gl::GetInternalFormatInfo(depthStencilBufferInternalFormat); + + egl::Config config; + config.renderTargetFormat = colorBufferInternalFormat; + config.depthStencilFormat = depthStencilBufferInternalFormat; + config.bufferSize = colorBufferFormatInfo.pixelBytes * 8; + config.redSize = colorBufferFormatInfo.redBits; + config.greenSize = colorBufferFormatInfo.greenBits; + config.blueSize = colorBufferFormatInfo.blueBits; + config.luminanceSize = colorBufferFormatInfo.luminanceBits; + config.alphaSize = colorBufferFormatInfo.alphaBits; + config.alphaMaskSize = 0; + config.bindToTextureRGB = (colorBufferFormatInfo.format == GL_RGB); + config.bindToTextureRGBA = (colorBufferFormatInfo.format == GL_RGBA || + colorBufferFormatInfo.format == GL_BGRA_EXT); + config.colorBufferType = EGL_RGB_BUFFER; + config.configCaveat = EGL_NONE; + config.configID = static_cast(configs.size() + 1); + // Can only support a conformant ES2 with feature level greater than 10.0. + config.conformant = (mRenderer11DeviceCaps.featureLevel >= D3D_FEATURE_LEVEL_10_0) + ? (EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT_KHR) + : 0; + + // PresentPathFast may not be conformant + if (mPresentPathFastEnabled) + { + config.conformant = 0; + } + + config.depthSize = depthStencilBufferFormatInfo.depthBits; + config.level = 0; + config.matchNativePixmap = EGL_NONE; + config.maxPBufferWidth = rendererCaps.max2DTextureSize; + config.maxPBufferHeight = rendererCaps.max2DTextureSize; + config.maxPBufferPixels = rendererCaps.max2DTextureSize * rendererCaps.max2DTextureSize; + config.maxSwapInterval = 4; + config.minSwapInterval = 0; + config.nativeRenderable = EGL_FALSE; + config.nativeVisualID = 0; + config.nativeVisualType = EGL_NONE; + // Can't support ES3 at all without feature level 10.0 + config.renderableType = + EGL_OPENGL_ES2_BIT | ((mRenderer11DeviceCaps.featureLevel >= D3D_FEATURE_LEVEL_10_0) + ? EGL_OPENGL_ES3_BIT_KHR + : 0); + config.sampleBuffers = 0; // FIXME: enumerate multi-sampling + config.samples = 0; + config.stencilSize = depthStencilBufferFormatInfo.stencilBits; + config.surfaceType = EGL_PBUFFER_BIT | EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT; + config.transparentType = EGL_NONE; + config.transparentRedValue = 0; + config.transparentGreenValue = 0; + config.transparentBlueValue = 0; + config.optimalOrientation = optimalSurfaceOrientation; + + configs.add(config); } } @@ -1006,13 +1003,13 @@ void Renderer11::generateDisplayExtensions(egl::DisplayExtensions *outExtensions outExtensions->surfaceD3DTexture2DShareHandle = true; } -#ifdef ANGLE_ENABLE_KEYEDMUTEX outExtensions->keyedMutex = true; -#endif - outExtensions->querySurfacePointer = true; outExtensions->windowFixedSize = true; + // If present path fast is active then the surface orientation extension isn't supported + outExtensions->surfaceOrientation = !mPresentPathFastEnabled; + // D3D11 does not support present with dirty rectangles until DXGI 1.2. outExtensions->postSubBuffer = mRenderer11DeviceCaps.supportsDXGI1_2; @@ -1020,11 +1017,16 @@ void Renderer11::generateDisplayExtensions(egl::DisplayExtensions *outExtensions outExtensions->deviceQuery = true; + outExtensions->createContextNoError = true; + outExtensions->image = true; outExtensions->imageBase = true; outExtensions->glTexture2DImage = true; outExtensions->glTextureCubemapImage = true; outExtensions->glRenderbufferImage = true; + + outExtensions->flexibleSurfaceCompatibility = true; + outExtensions->directComposition = !!mDCompModule; } gl::Error Renderer11::flush() @@ -1076,9 +1078,26 @@ gl::Error Renderer11::finish() return gl::Error(GL_NO_ERROR); } -SwapChainD3D *Renderer11::createSwapChain(NativeWindow nativeWindow, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat) +SwapChainD3D *Renderer11::createSwapChain(NativeWindow nativeWindow, + HANDLE shareHandle, + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation) { - return new SwapChain11(this, nativeWindow, shareHandle, backBufferFormat, depthBufferFormat); + return new SwapChain11(this, nativeWindow, shareHandle, backBufferFormat, depthBufferFormat, + orientation); +} + +CompilerImpl *Renderer11::createCompiler() +{ + if (mRenderer11DeviceCaps.featureLevel <= D3D_FEATURE_LEVEL_9_3) + { + return new CompilerD3D(SH_HLSL_4_0_FL9_3_OUTPUT); + } + else + { + return new CompilerD3D(SH_HLSL_4_1_OUTPUT); + } } void *Renderer11::getD3DDevice() @@ -1224,7 +1243,7 @@ gl::Error Renderer11::setTexture(gl::SamplerType type, int index, gl::Texture *t ASSERT((type == gl::SAMPLER_PIXEL && static_cast(index) < getRendererCaps().maxTextureImageUnits) || (type == gl::SAMPLER_VERTEX && static_cast(index) < getRendererCaps().maxVertexTextureImageUnits)); - setShaderResource(type, index, textureSRV); + mStateManager.setShaderResource(type, index, textureSRV); return gl::Error(GL_NO_ERROR); } @@ -1358,25 +1377,55 @@ gl::Error Renderer11::setUniformBuffers(const gl::Data &data, return gl::Error(GL_NO_ERROR); } -gl::Error Renderer11::setRasterizerState(const gl::RasterizerState &rasterState) +gl::Error Renderer11::updateState(const gl::Data &data, GLenum drawMode) { - if (mForceSetRasterState || memcmp(&rasterState, &mCurRasterState, sizeof(gl::RasterizerState)) != 0) + // Applies the render target surface, depth stencil surface, viewport rectangle and + // scissor rectangle to the renderer + const gl::Framebuffer *framebufferObject = data.state->getDrawFramebuffer(); + ASSERT(framebufferObject && framebufferObject->checkStatus(data) == GL_FRAMEBUFFER_COMPLETE); + gl::Error error = applyRenderTarget(framebufferObject); + if (error.isError()) { - ID3D11RasterizerState *dxRasterState = NULL; - gl::Error error = mStateCache.getRasterizerState(rasterState, mScissorEnabled, &dxRasterState); - if (error.isError()) - { - return error; - } - - mDeviceContext->RSSetState(dxRasterState); - - mCurRasterState = rasterState; + return error; } - mForceSetRasterState = false; + // Set the present path state + const bool presentPathFastActive = + UsePresentPathFast(this, framebufferObject->getFirstColorbuffer()); + mStateManager.updatePresentPath(presentPathFastActive, + framebufferObject->getFirstColorbuffer()); - return gl::Error(GL_NO_ERROR); + // Setting viewport state + mStateManager.setViewport(data.caps, data.state->getViewport(), data.state->getNearPlane(), + data.state->getFarPlane()); + + // Setting scissor state + mStateManager.setScissorRectangle(data.state->getScissor(), data.state->isScissorTestEnabled()); + + // Applying rasterizer state to D3D11 device + int samples = framebufferObject->getSamples(data); + gl::RasterizerState rasterizer = data.state->getRasterizerState(); + rasterizer.pointDrawMode = (drawMode == GL_POINTS); + rasterizer.multiSample = (samples != 0); + + error = mStateManager.setRasterizerState(rasterizer); + if (error.isError()) + { + return error; + } + + // Setting blend state + unsigned int mask = GetBlendSampleMask(data, samples); + error = mStateManager.setBlendState(framebufferObject, data.state->getBlendState(), + data.state->getBlendColor(), mask); + if (error.isError()) + { + return error; + } + + // Setting depth stencil state + error = mStateManager.setDepthStencilState(*data.state); + return error; } void Renderer11::syncState(const gl::State &state, const gl::State::DirtyBits &bitmask) @@ -1384,180 +1433,6 @@ void Renderer11::syncState(const gl::State &state, const gl::State::DirtyBits &b mStateManager.syncState(state, bitmask); } -gl::Error Renderer11::setBlendState(const gl::Framebuffer *framebuffer, - const gl::BlendState &blendState, - const gl::ColorF &blendColor, - unsigned int sampleMask) -{ - return mStateManager.setBlendState(framebuffer, blendState, blendColor, sampleMask); -} - -gl::Error Renderer11::setDepthStencilState(const gl::DepthStencilState &depthStencilState, int stencilRef, - int stencilBackRef, bool frontFaceCCW) -{ - if (mForceSetDepthStencilState || - memcmp(&depthStencilState, &mCurDepthStencilState, sizeof(gl::DepthStencilState)) != 0 || - stencilRef != mCurStencilRef || stencilBackRef != mCurStencilBackRef) - { - // get the maximum size of the stencil ref - unsigned int maxStencil = 0; - if (depthStencilState.stencilTest && mCurStencilSize > 0) - { - maxStencil = (1 << mCurStencilSize) - 1; - } - ASSERT((depthStencilState.stencilWritemask & maxStencil) == - (depthStencilState.stencilBackWritemask & maxStencil)); - ASSERT(stencilRef == stencilBackRef); - ASSERT((depthStencilState.stencilMask & maxStencil) == - (depthStencilState.stencilBackMask & maxStencil)); - - ID3D11DepthStencilState *dxDepthStencilState = NULL; - gl::Error error = mStateCache.getDepthStencilState(depthStencilState, &dxDepthStencilState); - if (error.isError()) - { - return error; - } - - ASSERT(dxDepthStencilState); - - // Max D3D11 stencil reference value is 0xFF, corresponding to the max 8 bits in a stencil buffer - // GL specifies we should clamp the ref value to the nearest bit depth when doing stencil ops - static_assert(D3D11_DEFAULT_STENCIL_READ_MASK == 0xFF, "Unexpected value of D3D11_DEFAULT_STENCIL_READ_MASK"); - static_assert(D3D11_DEFAULT_STENCIL_WRITE_MASK == 0xFF, "Unexpected value of D3D11_DEFAULT_STENCIL_WRITE_MASK"); - UINT dxStencilRef = std::min(stencilRef, 0xFFu); - - mDeviceContext->OMSetDepthStencilState(dxDepthStencilState, dxStencilRef); - - mCurDepthStencilState = depthStencilState; - mCurStencilRef = stencilRef; - mCurStencilBackRef = stencilBackRef; - } - - mForceSetDepthStencilState = false; - - return gl::Error(GL_NO_ERROR); -} - -void Renderer11::setScissorRectangle(const gl::Rectangle &scissor, bool enabled) -{ - if (mForceSetScissor || memcmp(&scissor, &mCurScissor, sizeof(gl::Rectangle)) != 0 || - enabled != mScissorEnabled) - { - if (enabled) - { - D3D11_RECT rect; - rect.left = std::max(0, scissor.x); - rect.top = std::max(0, scissor.y); - rect.right = scissor.x + std::max(0, scissor.width); - rect.bottom = scissor.y + std::max(0, scissor.height); - - mDeviceContext->RSSetScissorRects(1, &rect); - } - - if (enabled != mScissorEnabled) - { - mForceSetRasterState = true; - } - - mCurScissor = scissor; - mScissorEnabled = enabled; - } - - mForceSetScissor = false; -} - -void Renderer11::setViewport(const gl::Rectangle &viewport, float zNear, float zFar, GLenum drawMode, GLenum frontFace, - bool ignoreViewport) -{ - gl::Rectangle actualViewport = viewport; - float actualZNear = gl::clamp01(zNear); - float actualZFar = gl::clamp01(zFar); - if (ignoreViewport) - { - actualViewport.x = 0; - actualViewport.y = 0; - actualViewport.width = static_cast(mRenderTargetDesc.width); - actualViewport.height = static_cast(mRenderTargetDesc.height); - actualZNear = 0.0f; - actualZFar = 1.0f; - } - - bool viewportChanged = mForceSetViewport || memcmp(&actualViewport, &mCurViewport, sizeof(gl::Rectangle)) != 0 || - actualZNear != mCurNear || actualZFar != mCurFar; - - if (viewportChanged) - { - const gl::Caps& caps = getRendererCaps(); - - int dxMaxViewportBoundsX = static_cast(caps.maxViewportWidth); - int dxMaxViewportBoundsY = static_cast(caps.maxViewportHeight); - int dxMinViewportBoundsX = -dxMaxViewportBoundsX; - int dxMinViewportBoundsY = -dxMaxViewportBoundsY; - - if (mRenderer11DeviceCaps.featureLevel <= D3D_FEATURE_LEVEL_9_3) - { - // Feature Level 9 viewports shouldn't exceed the dimensions of the rendertarget. - dxMaxViewportBoundsX = static_cast(mRenderTargetDesc.width); - dxMaxViewportBoundsY = static_cast(mRenderTargetDesc.height); - dxMinViewportBoundsX = 0; - dxMinViewportBoundsY = 0; - } - - int dxViewportTopLeftX = gl::clamp(actualViewport.x, dxMinViewportBoundsX, dxMaxViewportBoundsX); - int dxViewportTopLeftY = gl::clamp(actualViewport.y, dxMinViewportBoundsY, dxMaxViewportBoundsY); - int dxViewportWidth = gl::clamp(actualViewport.width, 0, dxMaxViewportBoundsX - dxViewportTopLeftX); - int dxViewportHeight = gl::clamp(actualViewport.height, 0, dxMaxViewportBoundsY - dxViewportTopLeftY); - - D3D11_VIEWPORT dxViewport; - dxViewport.TopLeftX = static_cast(dxViewportTopLeftX); - dxViewport.TopLeftY = static_cast(dxViewportTopLeftY); - dxViewport.Width = static_cast(dxViewportWidth); - dxViewport.Height = static_cast(dxViewportHeight); - dxViewport.MinDepth = actualZNear; - dxViewport.MaxDepth = actualZFar; - - mDeviceContext->RSSetViewports(1, &dxViewport); - - mCurViewport = actualViewport; - mCurNear = actualZNear; - mCurFar = actualZFar; - - // On Feature Level 9_*, we must emulate large and/or negative viewports in the shaders using viewAdjust (like the D3D9 renderer). - if (mRenderer11DeviceCaps.featureLevel <= D3D_FEATURE_LEVEL_9_3) - { - mVertexConstants.viewAdjust[0] = static_cast((actualViewport.width - dxViewportWidth) + 2 * (actualViewport.x - dxViewportTopLeftX)) / dxViewport.Width; - mVertexConstants.viewAdjust[1] = static_cast((actualViewport.height - dxViewportHeight) + 2 * (actualViewport.y - dxViewportTopLeftY)) / dxViewport.Height; - mVertexConstants.viewAdjust[2] = static_cast(actualViewport.width) / dxViewport.Width; - mVertexConstants.viewAdjust[3] = static_cast(actualViewport.height) / dxViewport.Height; - } - - mPixelConstants.viewCoords[0] = actualViewport.width * 0.5f; - mPixelConstants.viewCoords[1] = actualViewport.height * 0.5f; - mPixelConstants.viewCoords[2] = actualViewport.x + (actualViewport.width * 0.5f); - mPixelConstants.viewCoords[3] = actualViewport.y + (actualViewport.height * 0.5f); - - // Instanced pointsprite emulation requires ViewCoords to be defined in the - // the vertex shader. - mVertexConstants.viewCoords[0] = mPixelConstants.viewCoords[0]; - mVertexConstants.viewCoords[1] = mPixelConstants.viewCoords[1]; - mVertexConstants.viewCoords[2] = mPixelConstants.viewCoords[2]; - mVertexConstants.viewCoords[3] = mPixelConstants.viewCoords[3]; - - mPixelConstants.depthFront[0] = (actualZFar - actualZNear) * 0.5f; - mPixelConstants.depthFront[1] = (actualZNear + actualZFar) * 0.5f; - - mVertexConstants.depthRange[0] = actualZNear; - mVertexConstants.depthRange[1] = actualZFar; - mVertexConstants.depthRange[2] = actualZFar - actualZNear; - - mPixelConstants.depthRange[0] = actualZNear; - mPixelConstants.depthRange[1] = actualZFar; - mPixelConstants.depthRange[2] = actualZFar - actualZNear; - } - - mForceSetViewport = false; -} - bool Renderer11::applyPrimitiveType(GLenum mode, GLsizei count, bool usesPointSize) { D3D11_PRIMITIVE_TOPOLOGY primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; @@ -1596,167 +1471,17 @@ bool Renderer11::applyPrimitiveType(GLenum mode, GLsizei count, bool usesPointSi return count >= minCount; } -void Renderer11::unsetConflictingSRVs(gl::SamplerType samplerType, uintptr_t resource, const gl::ImageIndex &index) -{ - auto ¤tSRVs = (samplerType == gl::SAMPLER_VERTEX ? mCurVertexSRVs : mCurPixelSRVs); - - for (size_t resourceIndex = 0; resourceIndex < currentSRVs.size(); ++resourceIndex) - { - auto &record = currentSRVs[resourceIndex]; - - if (record.srv && record.resource == resource && ImageIndexConflictsWithSRV(index, record.desc)) - { - setShaderResource(samplerType, static_cast(resourceIndex), NULL); - } - } -} - gl::Error Renderer11::applyRenderTarget(const gl::Framebuffer *framebuffer) { - // Get the color render buffer and serial - // Also extract the render target dimensions and view - unsigned int renderTargetWidth = 0; - unsigned int renderTargetHeight = 0; - DXGI_FORMAT renderTargetFormat = DXGI_FORMAT_UNKNOWN; - ID3D11RenderTargetView* framebufferRTVs[gl::IMPLEMENTATION_MAX_DRAW_BUFFERS] = {NULL}; - bool missingColorRenderTarget = true; - - const FramebufferD3D *framebufferD3D = GetImplAs(framebuffer); - const gl::AttachmentList &colorbuffers = framebufferD3D->getColorAttachmentsForRender(getWorkarounds()); - - for (size_t colorAttachment = 0; colorAttachment < colorbuffers.size(); ++colorAttachment) - { - const gl::FramebufferAttachment *colorbuffer = colorbuffers[colorAttachment]; - - if (colorbuffer) - { - // the draw buffer must be either "none", "back" for the default buffer or the same index as this color (in order) - - // check for zero-sized default framebuffer, which is a special case. - // in this case we do not wish to modify any state and just silently return false. - // this will not report any gl error but will cause the calling method to return. - if (colorbuffer->getWidth() == 0 || colorbuffer->getHeight() == 0) - { - return gl::Error(GL_NO_ERROR); - } - - // Extract the render target dimensions and view - RenderTarget11 *renderTarget = NULL; - gl::Error error = colorbuffer->getRenderTarget(&renderTarget); - if (error.isError()) - { - return error; - } - ASSERT(renderTarget); - - framebufferRTVs[colorAttachment] = renderTarget->getRenderTargetView(); - ASSERT(framebufferRTVs[colorAttachment]); - - if (missingColorRenderTarget) - { - renderTargetWidth = renderTarget->getWidth(); - renderTargetHeight = renderTarget->getHeight(); - renderTargetFormat = renderTarget->getDXGIFormat(); - missingColorRenderTarget = false; - } - - // Unbind render target SRVs from the shader here to prevent D3D11 warnings. - if (colorbuffer->type() == GL_TEXTURE) - { - uintptr_t rtResource = reinterpret_cast(GetViewResource(framebufferRTVs[colorAttachment])); - const gl::ImageIndex &index = colorbuffer->getTextureImageIndex(); - // The index doesn't need to be corrected for the small compressed texture workaround - // because a rendertarget is never compressed. - unsetConflictingSRVs(gl::SAMPLER_VERTEX, rtResource, index); - unsetConflictingSRVs(gl::SAMPLER_PIXEL, rtResource, index); - } - } - } - - // Get the depth stencil buffers - ID3D11DepthStencilView* framebufferDSV = NULL; - const gl::FramebufferAttachment *depthStencil = framebuffer->getDepthOrStencilbuffer(); - if (depthStencil) - { - RenderTarget11 *depthStencilRenderTarget = NULL; - gl::Error error = depthStencil->getRenderTarget(&depthStencilRenderTarget); - if (error.isError()) - { - SafeRelease(framebufferRTVs); - return error; - } - ASSERT(depthStencilRenderTarget); - - framebufferDSV = depthStencilRenderTarget->getDepthStencilView(); - ASSERT(framebufferDSV); - - // If there is no render buffer, the width, height and format values come from - // the depth stencil - if (missingColorRenderTarget) - { - renderTargetWidth = depthStencilRenderTarget->getWidth(); - renderTargetHeight = depthStencilRenderTarget->getHeight(); - renderTargetFormat = depthStencilRenderTarget->getDXGIFormat(); - } - - // Unbind render target SRVs from the shader here to prevent D3D11 warnings. - if (depthStencil->type() == GL_TEXTURE) - { - uintptr_t depthStencilResource = reinterpret_cast(GetViewResource(framebufferDSV)); - const gl::ImageIndex &index = depthStencil->getTextureImageIndex(); - // The index doesn't need to be corrected for the small compressed texture workaround - // because a rendertarget is never compressed. - unsetConflictingSRVs(gl::SAMPLER_VERTEX, depthStencilResource, index); - unsetConflictingSRVs(gl::SAMPLER_PIXEL, depthStencilResource, index); - } - - unsigned int stencilSize = depthStencil->getStencilSize(); - if (!mDepthStencilInitialized || stencilSize != mCurStencilSize) - { - mCurStencilSize = stencilSize; - mForceSetDepthStencilState = true; - } - } - - // Apply the render target and depth stencil - if (!mRenderTargetDescInitialized || !mDepthStencilInitialized || - memcmp(framebufferRTVs, mAppliedRTVs, sizeof(framebufferRTVs)) != 0 || - reinterpret_cast(framebufferDSV) != mAppliedDSV) - { - mDeviceContext->OMSetRenderTargets(getRendererCaps().maxDrawBuffers, framebufferRTVs, framebufferDSV); - - mRenderTargetDesc.width = renderTargetWidth; - mRenderTargetDesc.height = renderTargetHeight; - mRenderTargetDesc.format = renderTargetFormat; - mForceSetViewport = true; - mForceSetScissor = true; - mStateManager.forceSetBlendState(); - - if (!mDepthStencilInitialized) - { - mForceSetRasterState = true; - } - - for (size_t rtIndex = 0; rtIndex < ArraySize(framebufferRTVs); rtIndex++) - { - mAppliedRTVs[rtIndex] = reinterpret_cast(framebufferRTVs[rtIndex]); - } - mAppliedDSV = reinterpret_cast(framebufferDSV); - mRenderTargetDescInitialized = true; - mDepthStencilInitialized = true; - } - - const Framebuffer11 *framebuffer11 = GetImplAs(framebuffer); - gl::Error error = framebuffer11->invalidateSwizzles(); - if (error.isError()) - { - return error; - } - - return gl::Error(GL_NO_ERROR); + return mStateManager.syncFramebuffer(framebuffer); } -gl::Error Renderer11::applyVertexBuffer(const gl::State &state, GLenum mode, GLint first, GLsizei count, GLsizei instances, SourceIndexData *sourceInfo) +gl::Error Renderer11::applyVertexBuffer(const gl::State &state, + GLenum mode, + GLint first, + GLsizei count, + GLsizei instances, + TranslatedIndexData *indexInfo) { gl::Error error = mVertexDataManager->prepareVertexData(state, first, count, &mTranslatedAttribCache, instances); if (error.isError()) @@ -1765,12 +1490,18 @@ gl::Error Renderer11::applyVertexBuffer(const gl::State &state, GLenum mode, GLi } // If index information is passed, mark it with the current changed status. - if (sourceInfo) + if (indexInfo) { - sourceInfo->srcIndicesChanged = mAppliedIBChanged; + indexInfo->srcIndexData.srcIndicesChanged = mAppliedIBChanged; } - return mInputLayoutCache.applyVertexBuffers(mTranslatedAttribCache, mode, state.getProgram(), sourceInfo); + GLsizei numIndicesPerInstance = 0; + if (instances > 0) + { + numIndicesPerInstance = count; + } + return mInputLayoutCache.applyVertexBuffers(mTranslatedAttribCache, mode, state.getProgram(), + indexInfo, numIndicesPerInstance); } gl::Error Renderer11::applyIndexBuffer(const gl::Data &data, @@ -1778,14 +1509,13 @@ gl::Error Renderer11::applyIndexBuffer(const gl::Data &data, GLsizei count, GLenum mode, GLenum type, - TranslatedIndexData *indexInfo, - SourceIndexData *sourceIndexInfo) + TranslatedIndexData *indexInfo) { gl::VertexArray *vao = data.state->getVertexArray(); gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get(); - gl::Error error = mIndexDataManager->prepareIndexData(type, count, elementArrayBuffer, indices, - indexInfo, sourceIndexInfo, - data.state->isPrimitiveRestartEnabled()); + gl::Error error = + mIndexDataManager->prepareIndexData(type, count, elementArrayBuffer, indices, indexInfo, + data.state->isPrimitiveRestartEnabled()); if (error.isError()) { return error; @@ -1956,15 +1686,39 @@ gl::Error Renderer11::drawArraysImpl(const gl::Data &data, return drawTriangleFan(data, count, GL_NONE, nullptr, 0, instances); } - if (instances > 0) - { - mDeviceContext->DrawInstanced(count, instances, 0, 0); - return gl::Error(GL_NO_ERROR); - } - bool useInstancedPointSpriteEmulation = programD3D->usesPointSize() && getWorkarounds().useInstancedPointSpriteEmulation; + if (instances > 0) + { + if (mode == GL_POINTS && useInstancedPointSpriteEmulation) + { + // If pointsprite emulation is used with glDrawArraysInstanced then we need to take a + // less efficent code path. + // Instanced rendering of emulated pointsprites requires a loop to draw each batch of + // points. An offset into the instanced data buffer is calculated and applied on each + // iteration to ensure all instances are rendered correctly. + + // Each instance being rendered requires the inputlayout cache to reapply buffers and + // offsets. + for (GLsizei i = 0; i < instances; i++) + { + gl::Error error = mInputLayoutCache.updateVertexOffsetsForPointSpritesEmulation(i); + if (error.isError()) + { + return error; + } + + mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0); + } + } + else + { + mDeviceContext->DrawInstanced(count, instances, 0, 0); + } + return gl::Error(GL_NO_ERROR); + } + // If the shader is writing to gl_PointSize, then pointsprites are being rendered. // Emulating instanced point sprites for FL9_3 requires the topology to be // D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead. @@ -1999,16 +1753,41 @@ gl::Error Renderer11::drawElementsImpl(const gl::Data &data, return drawTriangleFan(data, count, type, indices, minIndex, instances); } + const ProgramD3D *programD3D = GetImplAs(data.state->getProgram()); if (instances > 0) { - mDeviceContext->DrawIndexedInstanced(count, instances, 0, -minIndex, 0); + if (mode == GL_POINTS && programD3D->usesInstancedPointSpriteEmulation()) + { + // If pointsprite emulation is used with glDrawElementsInstanced then we need to take a + // less efficent code path. + // Instanced rendering of emulated pointsprites requires a loop to draw each batch of + // points. An offset into the instanced data buffer is calculated and applied on each + // iteration to ensure all instances are rendered correctly. + GLsizei elementsToRender = static_cast(indexInfo.indexRange.vertexCount()); + + // Each instance being rendered requires the inputlayout cache to reapply buffers and + // offsets. + for (GLsizei i = 0; i < instances; i++) + { + gl::Error error = mInputLayoutCache.updateVertexOffsetsForPointSpritesEmulation(i); + if (error.isError()) + { + return error; + } + + mDeviceContext->DrawIndexedInstanced(6, elementsToRender, 0, 0, 0); + } + } + else + { + mDeviceContext->DrawIndexedInstanced(count, instances, 0, -minIndex, 0); + } return gl::Error(GL_NO_ERROR); } // If the shader is writing to gl_PointSize, then pointsprites are being rendered. // Emulating instanced point sprites for FL9_3 requires the topology to be // D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead. - const ProgramD3D *programD3D = GetImplAs(data.state->getProgram()); if (mode == GL_POINTS && programD3D->usesInstancedPointSpriteEmulation()) { // The count parameter passed to drawElements represents the total number of instances @@ -2420,7 +2199,7 @@ gl::Error Renderer11::applyUniforms(const ProgramD3D &programD3D, if (!mDriverConstantBufferVS) { D3D11_BUFFER_DESC constantBufferDescription = {0}; - constantBufferDescription.ByteWidth = sizeof(dx_VertexConstants); + constantBufferDescription.ByteWidth = sizeof(dx_VertexConstants11); constantBufferDescription.Usage = D3D11_USAGE_DEFAULT; constantBufferDescription.BindFlags = D3D11_BIND_CONSTANT_BUFFER; constantBufferDescription.CPUAccessFlags = 0; @@ -2439,7 +2218,7 @@ gl::Error Renderer11::applyUniforms(const ProgramD3D &programD3D, if (!mDriverConstantBufferPS) { D3D11_BUFFER_DESC constantBufferDescription = {0}; - constantBufferDescription.ByteWidth = sizeof(dx_PixelConstants); + constantBufferDescription.ByteWidth = sizeof(dx_PixelConstants11); constantBufferDescription.Usage = D3D11_USAGE_DEFAULT; constantBufferDescription.BindFlags = D3D11_BIND_CONSTANT_BUFFER; constantBufferDescription.CPUAccessFlags = 0; @@ -2455,23 +2234,27 @@ gl::Error Renderer11::applyUniforms(const ProgramD3D &programD3D, mDeviceContext->PSSetConstantBuffers(1, 1, &mDriverConstantBufferPS); } - if (memcmp(&mVertexConstants, &mAppliedVertexConstants, sizeof(dx_VertexConstants)) != 0) + const dx_VertexConstants11 &vertexConstants = mStateManager.getVertexConstants(); + if (memcmp(&vertexConstants, &mAppliedVertexConstants, sizeof(dx_VertexConstants11)) != 0) { ASSERT(mDriverConstantBufferVS != nullptr); if (mDriverConstantBufferVS) { - mDeviceContext->UpdateSubresource(mDriverConstantBufferVS, 0, NULL, &mVertexConstants, 16, 0); - memcpy(&mAppliedVertexConstants, &mVertexConstants, sizeof(dx_VertexConstants)); + mDeviceContext->UpdateSubresource(mDriverConstantBufferVS, 0, NULL, &vertexConstants, + 16, 0); + memcpy(&mAppliedVertexConstants, &vertexConstants, sizeof(dx_VertexConstants11)); } } - if (memcmp(&mPixelConstants, &mAppliedPixelConstants, sizeof(dx_PixelConstants)) != 0) + const dx_PixelConstants11 &pixelConstants = mStateManager.getPixelConstants(); + if (memcmp(&pixelConstants, &mAppliedPixelConstants, sizeof(dx_PixelConstants11)) != 0) { ASSERT(mDriverConstantBufferPS != nullptr); if (mDriverConstantBufferPS) { - mDeviceContext->UpdateSubresource(mDriverConstantBufferPS, 0, NULL, &mPixelConstants, 16, 0); - memcpy(&mAppliedPixelConstants, &mPixelConstants, sizeof(dx_PixelConstants)); + mDeviceContext->UpdateSubresource(mDriverConstantBufferPS, 0, NULL, &pixelConstants, 16, + 0); + memcpy(&mAppliedPixelConstants, &pixelConstants, sizeof(dx_PixelConstants11)); } } @@ -2497,45 +2280,25 @@ void Renderer11::markAllStateDirty() { TRACE_EVENT0("gpu.angle", "Renderer11::markAllStateDirty"); - for (size_t rtIndex = 0; rtIndex < ArraySize(mAppliedRTVs); rtIndex++) - { - mAppliedRTVs[rtIndex] = DirtyPointer; - } - mAppliedDSV = DirtyPointer; - mDepthStencilInitialized = false; - mRenderTargetDescInitialized = false; - - // We reset the current SRV data because it might not be in sync with D3D's state - // anymore. For example when a currently used SRV is used as an RTV, D3D silently - // remove it from its state. - mCurVertexSRVs.clear(); - mCurPixelSRVs.clear(); - - ASSERT(mForceSetVertexSamplerStates.size() == mCurVertexSRVs.size()); for (size_t vsamplerId = 0; vsamplerId < mForceSetVertexSamplerStates.size(); ++vsamplerId) { mForceSetVertexSamplerStates[vsamplerId] = true; } - ASSERT(mForceSetPixelSamplerStates.size() == mCurPixelSRVs.size()); for (size_t fsamplerId = 0; fsamplerId < mForceSetPixelSamplerStates.size(); ++fsamplerId) { mForceSetPixelSamplerStates[fsamplerId] = true; } - mStateManager.forceSetBlendState(); - mForceSetRasterState = true; - mForceSetDepthStencilState = true; - mForceSetScissor = true; - mForceSetViewport = true; + mStateManager.invalidateEverything(); mAppliedIB = NULL; mAppliedIBFormat = DXGI_FORMAT_UNKNOWN; mAppliedIBOffset = 0; - mAppliedVertexShader = DirtyPointer; - mAppliedGeometryShader = DirtyPointer; - mAppliedPixelShader = DirtyPointer; + mAppliedVertexShader = angle::DirtyPointer; + mAppliedGeometryShader = angle::DirtyPointer; + mAppliedPixelShader = angle::DirtyPointer; mAppliedNumXFBBindings = static_cast(-1); @@ -2545,8 +2308,8 @@ void Renderer11::markAllStateDirty() mAppliedTFOffsets[i] = 0; } - memset(&mAppliedVertexConstants, 0, sizeof(dx_VertexConstants)); - memset(&mAppliedPixelConstants, 0, sizeof(dx_PixelConstants)); + memset(&mAppliedVertexConstants, 0, sizeof(dx_VertexConstants11)); + memset(&mAppliedPixelConstants, 0, sizeof(dx_PixelConstants11)); mInputLayoutCache.markDirty(); @@ -2628,8 +2391,9 @@ bool Renderer11::testDeviceResettable() D3D_FEATURE_LEVEL dummyFeatureLevel; ID3D11DeviceContext* dummyContext; + ASSERT(mRequestedDriverType != D3D_DRIVER_TYPE_UNKNOWN); HRESULT result = D3D11CreateDevice( - NULL, mDriverType, NULL, + NULL, mRequestedDriverType, NULL, #if defined(_DEBUG) D3D11_CREATE_DEVICE_DEBUG, #else @@ -2655,6 +2419,13 @@ void Renderer11::release() releaseDeviceResources(); + if (!mCreatedWithDeviceEXT) + { + // Only delete the device if the Renderer11 owns it + // Otherwise we should keep it around in case we try to reinitialize the renderer later + SafeDelete(mEGLDevice); + } + SafeRelease(mDxgiFactory); SafeRelease(mDxgiAdapter); @@ -2682,7 +2453,15 @@ void Renderer11::release() mDxgiModule = NULL; } + if (mDCompModule) + { + FreeLibrary(mDCompModule); + mDCompModule = NULL; + } + mCompiler.release(); + + mSupportsShareHandles.reset(); } bool Renderer11::resetDevice() @@ -2763,33 +2542,94 @@ unsigned int Renderer11::getReservedFragmentUniformBuffers() const return 2; } +d3d11::ANGLED3D11DeviceType Renderer11::getDeviceType() const +{ + if (mCreatedWithDeviceEXT) + { + return d3d11::GetDeviceType(mDevice); + } + + if ((mRequestedDriverType == D3D_DRIVER_TYPE_SOFTWARE) || + (mRequestedDriverType == D3D_DRIVER_TYPE_REFERENCE) || + (mRequestedDriverType == D3D_DRIVER_TYPE_NULL)) + { + return d3d11::ANGLE_D3D11_DEVICE_TYPE_SOFTWARE_REF_OR_NULL; + } + + if (mRequestedDriverType == D3D_DRIVER_TYPE_WARP) + { + return d3d11::ANGLE_D3D11_DEVICE_TYPE_WARP; + } + + return d3d11::ANGLE_D3D11_DEVICE_TYPE_HARDWARE; +} + bool Renderer11::getShareHandleSupport() const { + if (mSupportsShareHandles.valid()) + { + return mSupportsShareHandles.value(); + } + // We only currently support share handles with BGRA surfaces, because // chrome needs BGRA. Once chrome fixes this, we should always support them. if (!getRendererExtensions().textureFormatBGRA8888) { + mSupportsShareHandles = false; return false; } // PIX doesn't seem to support using share handles, so disable them. if (gl::DebugAnnotationsActive()) { + mSupportsShareHandles = false; return false; } // Also disable share handles on Feature Level 9_3, since it doesn't support share handles on RGBA8 textures/swapchains. if (mRenderer11DeviceCaps.featureLevel <= D3D_FEATURE_LEVEL_9_3) { + mSupportsShareHandles = false; return false; } - // Also disable on non-hardware drivers, since sharing doesn't work cross-driver. - if (mDriverType != D3D_DRIVER_TYPE_HARDWARE) + // Find out which type of D3D11 device the Renderer11 is using + d3d11::ANGLED3D11DeviceType deviceType = getDeviceType(); + if (deviceType == d3d11::ANGLE_D3D11_DEVICE_TYPE_UNKNOWN) { + mSupportsShareHandles = false; return false; } + if (deviceType == d3d11::ANGLE_D3D11_DEVICE_TYPE_SOFTWARE_REF_OR_NULL) + { + // Software/Reference/NULL devices don't support share handles + mSupportsShareHandles = false; + return false; + } + + if (deviceType == d3d11::ANGLE_D3D11_DEVICE_TYPE_WARP) + { +#ifndef ANGLE_ENABLE_WINDOWS_STORE + if (!IsWindows8OrGreater()) + { + // WARP on Windows 7 doesn't support shared handles + mSupportsShareHandles = false; + return false; + } +#endif // ANGLE_ENABLE_WINDOWS_STORE + + // WARP on Windows 8.0+ supports shared handles when shared with another WARP device + // TODO: allow applications to query for HARDWARE or WARP-specific share handles, + // to prevent them trying to use a WARP share handle with an a HW device (or + // vice-versa) + // e.g. by creating EGL_D3D11_[HARDWARE/WARP]_DEVICE_SHARE_HANDLE_ANGLE + mSupportsShareHandles = true; + return true; + } + + ASSERT(mCreatedWithDeviceEXT || mRequestedDriverType == D3D_DRIVER_TYPE_HARDWARE); + mSupportsShareHandles = true; return true; } @@ -2875,12 +2715,20 @@ gl::Error Renderer11::copyImage2D(const gl::Framebuffer *framebuffer, const gl:: gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1); gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1); + const bool invertSource = UsePresentPathFast(this, colorbuffer); + if (invertSource) + { + sourceArea.y = sourceSize.height - sourceRect.y; + sourceArea.height = -sourceArea.height; + } + gl::Box destArea(destOffset.x, destOffset.y, 0, sourceRect.width, sourceRect.height, 1); gl::Extents destSize(destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1); // Use nearest filtering because source and destination are the same size for the direct // copy - error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, destFormat, GL_NEAREST); + error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, + destFormat, GL_NEAREST, false); if (error.isError()) { return error; @@ -2926,12 +2774,20 @@ gl::Error Renderer11::copyImageCube(const gl::Framebuffer *framebuffer, const gl gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1); gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1); + const bool invertSource = UsePresentPathFast(this, colorbuffer); + if (invertSource) + { + sourceArea.y = sourceSize.height - sourceRect.y; + sourceArea.height = -sourceArea.height; + } + gl::Box destArea(destOffset.x, destOffset.y, 0, sourceRect.width, sourceRect.height, 1); gl::Extents destSize(destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1); // Use nearest filtering because source and destination are the same size for the direct // copy - error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, destFormat, GL_NEAREST); + error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, + destFormat, GL_NEAREST, false); if (error.isError()) { return error; @@ -2982,7 +2838,8 @@ gl::Error Renderer11::copyImage3D(const gl::Framebuffer *framebuffer, const gl:: // Use nearest filtering because source and destination are the same size for the direct // copy - error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, destFormat, GL_NEAREST); + error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, + destFormat, GL_NEAREST, false); if (error.isError()) { return error; @@ -3033,7 +2890,8 @@ gl::Error Renderer11::copyImage2DArray(const gl::Framebuffer *framebuffer, const // Use nearest filtering because source and destination are the same size for the direct // copy - error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, destFormat, GL_NEAREST); + error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, + destFormat, GL_NEAREST, false); if (error.isError()) { return error; @@ -3061,9 +2919,9 @@ void Renderer11::setOneTimeRenderTarget(ID3D11RenderTargetView *renderTargetView // Do not preserve the serial for this one-time-use render target for (size_t rtIndex = 0; rtIndex < ArraySize(mAppliedRTVs); rtIndex++) { - mAppliedRTVs[rtIndex] = DirtyPointer; + mAppliedRTVs[rtIndex] = angle::DirtyPointer; } - mAppliedDSV = DirtyPointer; + mAppliedDSV = angle::DirtyPointer; } UINT64 EstimateSize(D3D11_TEXTURE2D_DESC &desc) @@ -3611,28 +3469,53 @@ RenderbufferImpl *Renderer11::createRenderbuffer() return renderbuffer; } -gl::Error Renderer11::readTextureData(ID3D11Texture2D *texture, unsigned int subResource, const gl::Rectangle &area, GLenum format, - GLenum type, GLuint outputPitch, const gl::PixelPackState &pack, uint8_t *pixels) +gl::Error Renderer11::readFromAttachment(const gl::FramebufferAttachment &srcAttachment, + const gl::Rectangle &sourceArea, + GLenum format, + GLenum type, + GLuint outputPitch, + const gl::PixelPackState &pack, + uint8_t *pixelsOut) { - ASSERT(area.width >= 0); - ASSERT(area.height >= 0); + ASSERT(sourceArea.width >= 0); + ASSERT(sourceArea.height >= 0); - D3D11_TEXTURE2D_DESC textureDesc; - texture->GetDesc(&textureDesc); + const bool invertTexture = UsePresentPathFast(this, &srcAttachment); + + RenderTargetD3D *renderTarget = nullptr; + gl::Error error = srcAttachment.getRenderTarget(&renderTarget); + if (error.isError()) + { + return error; + } + + RenderTarget11 *rt11 = GetAs(renderTarget); + ASSERT(rt11->getTexture()); + + TextureHelper11 textureHelper = TextureHelper11::MakeAndReference(rt11->getTexture()); + unsigned int sourceSubResource = rt11->getSubresourceIndex(); + + const gl::Extents &texSize = textureHelper.getExtents(); + + gl::Rectangle actualArea = sourceArea; + if (invertTexture) + { + actualArea.y = texSize.height - actualArea.y - actualArea.height; + } // Clamp read region to the defined texture boundaries, preventing out of bounds reads // and reads of uninitialized data. gl::Rectangle safeArea; - safeArea.x = gl::clamp(area.x, 0, static_cast(textureDesc.Width)); - safeArea.y = gl::clamp(area.y, 0, static_cast(textureDesc.Height)); - safeArea.width = gl::clamp(area.width + std::min(area.x, 0), 0, - static_cast(textureDesc.Width) - safeArea.x); - safeArea.height = gl::clamp(area.height + std::min(area.y, 0), 0, - static_cast(textureDesc.Height) - safeArea.y); + safeArea.x = gl::clamp(actualArea.x, 0, texSize.width); + safeArea.y = gl::clamp(actualArea.y, 0, texSize.height); + safeArea.width = + gl::clamp(actualArea.width + std::min(actualArea.x, 0), 0, texSize.width - safeArea.x); + safeArea.height = + gl::clamp(actualArea.height + std::min(actualArea.y, 0), 0, texSize.height - safeArea.y); ASSERT(safeArea.x >= 0 && safeArea.y >= 0); - ASSERT(safeArea.x + safeArea.width <= static_cast(textureDesc.Width)); - ASSERT(safeArea.y + safeArea.height <= static_cast(textureDesc.Height)); + ASSERT(safeArea.x + safeArea.width <= texSize.width); + ASSERT(safeArea.y + safeArea.height <= texSize.height); if (safeArea.width == 0 || safeArea.height == 0) { @@ -3640,35 +3523,29 @@ gl::Error Renderer11::readTextureData(ID3D11Texture2D *texture, unsigned int sub return gl::Error(GL_NO_ERROR); } - D3D11_TEXTURE2D_DESC stagingDesc; - stagingDesc.Width = safeArea.width; - stagingDesc.Height = safeArea.height; - stagingDesc.MipLevels = 1; - stagingDesc.ArraySize = 1; - stagingDesc.Format = textureDesc.Format; - stagingDesc.SampleDesc.Count = 1; - stagingDesc.SampleDesc.Quality = 0; - stagingDesc.Usage = D3D11_USAGE_STAGING; - stagingDesc.BindFlags = 0; - stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - stagingDesc.MiscFlags = 0; - - ID3D11Texture2D* stagingTex = NULL; - HRESULT result = mDevice->CreateTexture2D(&stagingDesc, NULL, &stagingTex); - if (FAILED(result)) + gl::Extents safeSize(safeArea.width, safeArea.height, 1); + auto errorOrResult = CreateStagingTexture(textureHelper.getTextureType(), + textureHelper.getFormat(), safeSize, mDevice); + if (errorOrResult.isError()) { - return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal staging texture for ReadPixels, HRESULT: 0x%X.", result); + return errorOrResult.getError(); } - ID3D11Texture2D* srcTex = NULL; - if (textureDesc.SampleDesc.Count > 1) + TextureHelper11 stagingHelper(errorOrResult.getResult()); + TextureHelper11 resolvedTextureHelper; + + // "srcTexture" usually points to the source texture. + // For 2D multisampled textures, it points to the multisampled resolve texture. + const TextureHelper11 *srcTexture = &textureHelper; + + if (textureHelper.getTextureType() == GL_TEXTURE_2D && textureHelper.getSampleCount() > 1) { D3D11_TEXTURE2D_DESC resolveDesc; - resolveDesc.Width = textureDesc.Width; - resolveDesc.Height = textureDesc.Height; + resolveDesc.Width = static_cast(texSize.width); + resolveDesc.Height = static_cast(texSize.height); resolveDesc.MipLevels = 1; resolveDesc.ArraySize = 1; - resolveDesc.Format = textureDesc.Format; + resolveDesc.Format = textureHelper.getFormat(); resolveDesc.SampleDesc.Count = 1; resolveDesc.SampleDesc.Quality = 0; resolveDesc.Usage = D3D11_USAGE_DEFAULT; @@ -3676,20 +3553,22 @@ gl::Error Renderer11::readTextureData(ID3D11Texture2D *texture, unsigned int sub resolveDesc.CPUAccessFlags = 0; resolveDesc.MiscFlags = 0; - result = mDevice->CreateTexture2D(&resolveDesc, NULL, &srcTex); + ID3D11Texture2D *resolveTex2D = nullptr; + HRESULT result = mDevice->CreateTexture2D(&resolveDesc, nullptr, &resolveTex2D); if (FAILED(result)) { - SafeRelease(stagingTex); - return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal resolve texture for ReadPixels, HRESULT: 0x%X.", result); + return gl::Error(GL_OUT_OF_MEMORY, + "Renderer11::readTextureData failed to create internal resolve " + "texture for ReadPixels, HRESULT: 0x%X.", + result); } - mDeviceContext->ResolveSubresource(srcTex, 0, texture, subResource, textureDesc.Format); - subResource = 0; - } - else - { - srcTex = texture; - srcTex->AddRef(); + mDeviceContext->ResolveSubresource(resolveTex2D, 0, textureHelper.getTexture2D(), + sourceSubResource, textureHelper.getFormat()); + resolvedTextureHelper = TextureHelper11::MakeAndReference(resolveTex2D); + + sourceSubResource = 0; + srcTexture = &resolvedTextureHelper; } D3D11_BOX srcBox; @@ -3697,28 +3576,52 @@ gl::Error Renderer11::readTextureData(ID3D11Texture2D *texture, unsigned int sub srcBox.right = static_cast(safeArea.x + safeArea.width); srcBox.top = static_cast(safeArea.y); srcBox.bottom = static_cast(safeArea.y + safeArea.height); - srcBox.front = 0; - srcBox.back = 1; - mDeviceContext->CopySubresourceRegion(stagingTex, 0, 0, 0, 0, srcTex, subResource, &srcBox); + // Select the correct layer from a 3D attachment + srcBox.front = 0; + if (textureHelper.getTextureType() == GL_TEXTURE_3D) + { + srcBox.front = static_cast(srcAttachment.layer()); + } + srcBox.back = srcBox.front + 1; - SafeRelease(srcTex); + mDeviceContext->CopySubresourceRegion(stagingHelper.getResource(), 0, 0, 0, 0, + srcTexture->getResource(), sourceSubResource, &srcBox); - PackPixelsParams packParams(safeArea, format, type, outputPitch, pack, 0); - gl::Error error = packPixels(stagingTex, packParams, pixels); + if (invertTexture) + { + gl::PixelPackState invertTexturePack; - SafeRelease(stagingTex); + // Create a new PixelPackState with reversed row order. Note that we can't just assign + // 'invertTexturePack' to be 'pack' (or memcpy) since that breaks the ref counting/object + // tracking in the 'pixelBuffer' members, causing leaks. Instead we must use + // pixelBuffer.set() twice, which performs the addRef/release correctly + invertTexturePack.alignment = pack.alignment; + invertTexturePack.pixelBuffer.set(pack.pixelBuffer.get()); + invertTexturePack.reverseRowOrder = !pack.reverseRowOrder; - return error; + PackPixelsParams packParams(safeArea, format, type, outputPitch, invertTexturePack, 0); + error = packPixels(stagingHelper, packParams, pixelsOut); + + invertTexturePack.pixelBuffer.set(nullptr); + + return error; + } + else + { + PackPixelsParams packParams(safeArea, format, type, outputPitch, pack, 0); + return packPixels(stagingHelper, packParams, pixelsOut); + } } -gl::Error Renderer11::packPixels(ID3D11Texture2D *readTexture, const PackPixelsParams ¶ms, uint8_t *pixelsOut) +gl::Error Renderer11::packPixels(const TextureHelper11 &textureHelper, + const PackPixelsParams ¶ms, + uint8_t *pixelsOut) { - D3D11_TEXTURE2D_DESC textureDesc; - readTexture->GetDesc(&textureDesc); + ID3D11Resource *readResource = textureHelper.getResource(); D3D11_MAPPED_SUBRESOURCE mapping; - HRESULT hr = mDeviceContext->Map(readTexture, 0, D3D11_MAP_READ, 0, &mapping); + HRESULT hr = mDeviceContext->Map(readResource, 0, D3D11_MAP_READ, 0, &mapping); if (FAILED(hr)) { ASSERT(hr == E_OUTOFMEMORY); @@ -3738,7 +3641,7 @@ gl::Error Renderer11::packPixels(ID3D11Texture2D *readTexture, const PackPixelsP inputPitch = static_cast(mapping.RowPitch); } - const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(textureDesc.Format); + const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(textureHelper.getFormat()); const gl::InternalFormat &sourceFormatInfo = gl::GetInternalFormatInfo(dxgiFormatInfo.internalFormat); if (sourceFormatInfo.format == params.format && sourceFormatInfo.type == params.type) { @@ -3750,9 +3653,8 @@ gl::Error Renderer11::packPixels(ID3D11Texture2D *readTexture, const PackPixelsP } else { - const d3d11::DXGIFormat &sourceDXGIFormatInfo = d3d11::GetDXGIFormatInfo(textureDesc.Format); - ColorCopyFunction fastCopyFunc = sourceDXGIFormatInfo.getFastCopyFunction(params.format, params.type); - + ColorCopyFunction fastCopyFunc = + dxgiFormatInfo.getFastCopyFunction(params.format, params.type); GLenum sizedDestInternalFormat = gl::GetSizedInternalFormat(params.format, params.type); const gl::InternalFormat &destFormatInfo = gl::GetInternalFormatInfo(sizedDestInternalFormat); @@ -3772,7 +3674,7 @@ gl::Error Renderer11::packPixels(ID3D11Texture2D *readTexture, const PackPixelsP } else { - ColorReadFunction colorReadFunction = sourceDXGIFormatInfo.colorReadFunction; + ColorReadFunction colorReadFunction = dxgiFormatInfo.colorReadFunction; ColorWriteFunction colorWriteFunction = GetColorWriteFunction(params.format, params.type); uint8_t temp[16]; // Maximum size of any Color type used. @@ -3797,14 +3699,20 @@ gl::Error Renderer11::packPixels(ID3D11Texture2D *readTexture, const PackPixelsP } } - mDeviceContext->Unmap(readTexture, 0); + mDeviceContext->Unmap(readResource, 0); return gl::Error(GL_NO_ERROR); } -gl::Error Renderer11::blitRenderbufferRect(const gl::Rectangle &readRect, const gl::Rectangle &drawRect, RenderTargetD3D *readRenderTarget, - RenderTargetD3D *drawRenderTarget, GLenum filter, const gl::Rectangle *scissor, - bool colorBlit, bool depthBlit, bool stencilBlit) +gl::Error Renderer11::blitRenderbufferRect(const gl::Rectangle &readRectIn, + const gl::Rectangle &drawRectIn, + RenderTargetD3D *readRenderTarget, + RenderTargetD3D *drawRenderTarget, + GLenum filter, + const gl::Rectangle *scissor, + bool colorBlit, + bool depthBlit, + bool stencilBlit) { // Since blitRenderbufferRect is called for each render buffer that needs to be blitted, // it should never be the case that both color and depth/stencil need to be blitted at @@ -3870,13 +3778,94 @@ gl::Error Renderer11::blitRenderbufferRect(const gl::Rectangle &readRect, const gl::Extents readSize(readRenderTarget->getWidth(), readRenderTarget->getHeight(), 1); gl::Extents drawSize(drawRenderTarget->getWidth(), drawRenderTarget->getHeight(), 1); + // From the spec: + // "The actual region taken from the read framebuffer is limited to the intersection of the + // source buffers being transferred, which may include the color buffer selected by the read + // buffer, the depth buffer, and / or the stencil buffer depending on mask." + // This means negative x and y are out of bounds, and not to be read from. We handle this here + // by internally scaling the read and draw rectangles. + gl::Rectangle readRect = readRectIn; + gl::Rectangle drawRect = drawRectIn; + auto readToDrawX = [&drawRectIn, &readRectIn](int readOffset) + { + double readToDrawScale = + static_cast(drawRectIn.width) / static_cast(readRectIn.width); + return static_cast(round(static_cast(readOffset) * readToDrawScale)); + }; + if (readRect.x < 0) + { + int readOffset = -readRect.x; + readRect.x += readOffset; + readRect.width -= readOffset; + + int drawOffset = readToDrawX(readOffset); + drawRect.x += drawOffset; + drawRect.width -= drawOffset; + } + + auto readToDrawY = [&drawRectIn, &readRectIn](int readOffset) + { + double readToDrawScale = + static_cast(drawRectIn.height) / static_cast(readRectIn.height); + return static_cast(round(static_cast(readOffset) * readToDrawScale)); + }; + if (readRect.y < 0) + { + int readOffset = -readRect.y; + readRect.y += readOffset; + readRect.height -= readOffset; + + int drawOffset = readToDrawY(readOffset); + drawRect.y += drawOffset; + drawRect.height -= drawOffset; + } + + if (readRect.x1() < 0) + { + int readOffset = -readRect.x1(); + readRect.width += readOffset; + + int drawOffset = readToDrawX(readOffset); + drawRect.width += drawOffset; + } + + if (readRect.y1() < 0) + { + int readOffset = -readRect.y1(); + readRect.height += readOffset; + + int drawOffset = readToDrawY(readOffset); + drawRect.height += drawOffset; + } + bool scissorNeeded = scissor && gl::ClipRectangle(drawRect, *scissor, NULL); - bool wholeBufferCopy = !scissorNeeded && - readRect.x == 0 && readRect.width == readSize.width && - readRect.y == 0 && readRect.height == readSize.height && - drawRect.x == 0 && drawRect.width == drawSize.width && - drawRect.y == 0 && drawRect.height == drawSize.height; + const auto &destFormatInfo = gl::GetInternalFormatInfo(drawRenderTarget->getInternalFormat()); + const auto &srcFormatInfo = gl::GetInternalFormatInfo(readRenderTarget->getInternalFormat()); + const auto &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(drawRenderTarget11->getDXGIFormat()); + + // Some blits require masking off emulated texture channels. eg: from RGBA8 to RGB8, we + // emulate RGB8 with RGBA8, so we need to mask off the alpha channel when we copy. + + gl::Color colorMask; + colorMask.red = (srcFormatInfo.redBits > 0) && (destFormatInfo.redBits == 0) && + (dxgiFormatInfo.redBits > 0); + colorMask.green = (srcFormatInfo.greenBits > 0) && (destFormatInfo.greenBits == 0) && + (dxgiFormatInfo.greenBits > 0); + colorMask.blue = (srcFormatInfo.blueBits > 0) && (destFormatInfo.blueBits == 0) && + (dxgiFormatInfo.blueBits > 0); + colorMask.alpha = (srcFormatInfo.alphaBits > 0) && (destFormatInfo.alphaBits == 0) && + (dxgiFormatInfo.alphaBits > 0); + + // We only currently support masking off the alpha channel. + bool colorMaskingNeeded = colorMask.alpha; + ASSERT(!colorMask.red && !colorMask.green && !colorMask.blue); + + bool wholeBufferCopy = !scissorNeeded && !colorMaskingNeeded && readRect.x == 0 && + readRect.width == readSize.width && readRect.y == 0 && + readRect.height == readSize.height && drawRect.x == 0 && + drawRect.width == drawSize.width && drawRect.y == 0 && + drawRect.height == drawSize.height; bool stretchRequired = readRect.width != drawRect.width || readRect.height != drawRect.height; @@ -3887,14 +3876,13 @@ gl::Error Renderer11::blitRenderbufferRect(const gl::Rectangle &readRect, const drawRect.x < 0 || drawRect.x + drawRect.width > drawSize.width || drawRect.y < 0 || drawRect.y + drawRect.height > drawSize.height; - const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(drawRenderTarget11->getDXGIFormat()); bool partialDSBlit = (dxgiFormatInfo.depthBits > 0 && depthBlit) != (dxgiFormatInfo.stencilBits > 0 && stencilBlit); gl::Error result(GL_NO_ERROR); if (readRenderTarget11->getDXGIFormat() == drawRenderTarget11->getDXGIFormat() && !stretchRequired && !outOfBounds && !flipRequired && !partialDSBlit && - (!(depthBlit || stencilBlit) || wholeBufferCopy)) + !colorMaskingNeeded && (!(depthBlit || stencilBlit) || wholeBufferCopy)) { UINT dstX = drawRect.x; UINT dstY = drawRect.y; @@ -3964,9 +3952,10 @@ gl::Error Renderer11::blitRenderbufferRect(const gl::Rectangle &readRect, const } else { - GLenum format = gl::GetInternalFormatInfo(drawRenderTarget->getInternalFormat()).format; + // We don't currently support masking off any other channel than alpha + bool maskOffAlpha = colorMaskingNeeded && colorMask.alpha; result = mBlit->copyTexture(readSRV, readArea, readSize, drawRTV, drawArea, drawSize, - scissor, format, filter); + scissor, destFormatInfo.format, filter, maskOffAlpha); } } @@ -4096,28 +4085,6 @@ WorkaroundsD3D Renderer11::generateWorkarounds() const return d3d11::GenerateWorkarounds(mRenderer11DeviceCaps.featureLevel); } -void Renderer11::setShaderResource(gl::SamplerType shaderType, UINT resourceSlot, ID3D11ShaderResourceView *srv) -{ - auto ¤tSRVs = (shaderType == gl::SAMPLER_VERTEX ? mCurVertexSRVs : mCurPixelSRVs); - - ASSERT(static_cast(resourceSlot) < currentSRVs.size()); - const SRVRecord &record = currentSRVs[resourceSlot]; - - if (record.srv != reinterpret_cast(srv)) - { - if (shaderType == gl::SAMPLER_VERTEX) - { - mDeviceContext->VSSetShaderResources(resourceSlot, 1, &srv); - } - else - { - mDeviceContext->PSSetShaderResources(resourceSlot, 1, &srv); - } - - currentSRVs.update(resourceSlot, srv); - } -} - void Renderer11::createAnnotator() { // The D3D11 renderer must choose the D3D9 debug annotator because the D3D11 interface @@ -4134,50 +4101,26 @@ void Renderer11::createAnnotator() gl::Error Renderer11::clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd) { - if (rangeStart == rangeEnd) - { - return gl::Error(GL_NO_ERROR); - } - - auto ¤tSRVs = (samplerType == gl::SAMPLER_VERTEX ? mCurVertexSRVs : mCurPixelSRVs); - - gl::Range clearRange(rangeStart, rangeStart); - clearRange.extend(std::min(rangeEnd, currentSRVs.highestUsed())); - - if (clearRange.empty()) - { - return gl::Error(GL_NO_ERROR); - } - - if (samplerType == gl::SAMPLER_VERTEX) - { - mDeviceContext->VSSetShaderResources(static_cast(rangeStart), - static_cast(rangeEnd - rangeStart), - &mNullSRVs[0]); - } - else - { - mDeviceContext->PSSetShaderResources(static_cast(rangeStart), - static_cast(rangeEnd - rangeStart), - &mNullSRVs[0]); - } - - for (size_t samplerIndex = rangeStart; samplerIndex < rangeEnd; ++samplerIndex) - { - currentSRVs.update(samplerIndex, nullptr); - } - - return gl::Error(GL_NO_ERROR); + return mStateManager.clearTextures(samplerType, rangeStart, rangeEnd); } -egl::Error Renderer11::initializeEGLDevice(DeviceD3D **outDevice) +egl::Error Renderer11::getEGLDevice(DeviceImpl **device) { - if (*outDevice == nullptr) + if (mEGLDevice == nullptr) { ASSERT(mDevice != nullptr); - *outDevice = new DeviceD3D(reinterpret_cast(mDevice), EGL_D3D11_DEVICE_ANGLE); + mEGLDevice = new DeviceD3D(); + egl::Error error = mEGLDevice->initialize(reinterpret_cast(mDevice), + EGL_D3D11_DEVICE_ANGLE, EGL_FALSE); + + if (error.isError()) + { + SafeDelete(mEGLDevice); + return error; + } } + *device = static_cast(mEGLDevice); return egl::Error(EGL_SUCCESS); } } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h index ee4543fa69..46d5c8c0bb 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h @@ -18,6 +18,7 @@ #include "libANGLE/renderer/d3d/RenderTargetD3D.h" #include "libANGLE/renderer/d3d/d3d11/DebugAnnotator11.h" #include "libANGLE/renderer/d3d/d3d11/InputLayoutCache.h" +#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h" #include "libANGLE/renderer/d3d/d3d11/RenderStateCache.h" #include "libANGLE/renderer/d3d/d3d11/StateManager11.h" @@ -111,7 +112,13 @@ class Renderer11 : public RendererD3D gl::Error flush() override; gl::Error finish() override; - virtual SwapChainD3D *createSwapChain(NativeWindow nativeWindow, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat); + SwapChainD3D *createSwapChain(NativeWindow nativeWindow, + HANDLE shareHandle, + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation) override; + + CompilerImpl *createCompiler() override; virtual gl::Error generateSwizzle(gl::Texture *texture); virtual gl::Error setSamplerState(gl::SamplerType type, int index, gl::Texture *texture, const gl::SamplerState &sampler); @@ -121,41 +128,32 @@ class Renderer11 : public RendererD3D const std::vector &vertexUniformBuffers, const std::vector &fragmentUniformBuffers) override; - virtual gl::Error setRasterizerState(const gl::RasterizerState &rasterState); - gl::Error setBlendState(const gl::Framebuffer *framebuffer, - const gl::BlendState &blendState, - const gl::ColorF &blendColor, - unsigned int sampleMask) override; - - virtual gl::Error setDepthStencilState(const gl::DepthStencilState &depthStencilState, int stencilRef, - int stencilBackRef, bool frontFaceCCW); - - virtual void setScissorRectangle(const gl::Rectangle &scissor, bool enabled); - virtual void setViewport(const gl::Rectangle &viewport, float zNear, float zFar, GLenum drawMode, GLenum frontFace, - bool ignoreViewport); + gl::Error updateState(const gl::Data &data, GLenum drawMode) override; virtual bool applyPrimitiveType(GLenum mode, GLsizei count, bool usesPointSize); gl::Error applyRenderTarget(const gl::Framebuffer *frameBuffer) override; gl::Error applyUniforms(const ProgramD3D &programD3D, GLenum drawMode, const std::vector &uniformArray) override; - virtual gl::Error applyVertexBuffer(const gl::State &state, GLenum mode, GLint first, GLsizei count, GLsizei instances, SourceIndexData *sourceIndexInfo); + virtual gl::Error applyVertexBuffer(const gl::State &state, + GLenum mode, + GLint first, + GLsizei count, + GLsizei instances, + TranslatedIndexData *indexInfo); gl::Error applyIndexBuffer(const gl::Data &data, const GLvoid *indices, GLsizei count, GLenum mode, GLenum type, - TranslatedIndexData *indexInfo, - SourceIndexData *sourceIndexInfo) override; + TranslatedIndexData *indexInfo) override; void applyTransformFeedbackBuffers(const gl::State &state) override; - virtual void markAllStateDirty(); - // lost device bool testDeviceLost() override; bool testDeviceResettable() override; - VendorID getVendorId() const override; + VendorID getVendorId() const; SIZE_T getMaxResourceSize() const; std::string getRendererDescription() const override; DeviceIdentifier getAdapterIdentifier() const override; @@ -259,18 +257,24 @@ class Renderer11 : public RendererD3D virtual gl::Error fastCopyBufferToTexture(const gl::PixelUnpackState &unpack, unsigned int offset, RenderTargetD3D *destRenderTarget, GLenum destinationFormat, GLenum sourcePixelsType, const gl::Box &destArea); + void markAllStateDirty(); void unapplyRenderTargets(); void setOneTimeRenderTarget(ID3D11RenderTargetView *renderTargetView); - gl::Error packPixels(ID3D11Texture2D *readTexture, const PackPixelsParams ¶ms, uint8_t *pixelsOut); + gl::Error packPixels(const TextureHelper11 &textureHelper, + const PackPixelsParams ¶ms, + uint8_t *pixelsOut); bool getLUID(LUID *adapterLuid) const override; VertexConversionType getVertexConversionType(gl::VertexFormatType vertexFormatType) const override; GLenum getVertexComponentType(gl::VertexFormatType vertexFormatType) const override; - gl::Error readTextureData(ID3D11Texture2D *texture, unsigned int subResource, const gl::Rectangle &area, GLenum format, - GLenum type, GLuint outputPitch, const gl::PixelPackState &pack, uint8_t *pixels); - - void setShaderResource(gl::SamplerType shaderType, UINT resourceSlot, ID3D11ShaderResourceView *srv); + gl::Error readFromAttachment(const gl::FramebufferAttachment &srcAttachment, + const gl::Rectangle &sourceArea, + GLenum format, + GLenum type, + GLuint outputPitch, + const gl::PixelPackState &pack, + uint8_t *pixels); gl::Error blitRenderbufferRect(const gl::Rectangle &readRect, const gl::Rectangle &drawRect, RenderTargetD3D *readRenderTarget, RenderTargetD3D *drawRenderTarget, GLenum filter, const gl::Rectangle *scissor, @@ -281,17 +285,18 @@ class Renderer11 : public RendererD3D RendererClass getRendererClass() const override { return RENDERER_D3D11; } InputLayoutCache *getInputLayoutCache() { return &mInputLayoutCache; } + StateManager11 *getStateManager() { return &mStateManager; } void onSwap(); void onBufferDelete(const Buffer11 *deleted); + egl::Error getEGLDevice(DeviceImpl **device) override; + protected: void createAnnotator() override; gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd) override; gl::Error applyShadersImpl(const gl::Data &data, GLenum drawMode) override; - egl::Error initializeEGLDevice(DeviceD3D **outDevice) override; - void syncState(const gl::State &state, const gl::State::DirtyBits &bitmask) override; private: @@ -327,7 +332,6 @@ class Renderer11 : public RendererD3D int instances); ID3D11Texture2D *resolveMultisampledTexture(ID3D11Texture2D *source, unsigned int subresource); - void unsetConflictingSRVs(gl::SamplerType shaderType, uintptr_t resource, const gl::ImageIndex &index); void populateRenderer11DeviceCaps(); @@ -335,31 +339,26 @@ class Renderer11 : public RendererD3D HMODULE mD3d11Module; HMODULE mDxgiModule; + HMODULE mDCompModule; std::vector mAvailableFeatureLevels; - D3D_DRIVER_TYPE mDriverType; + D3D_DRIVER_TYPE mRequestedDriverType; + bool mCreatedWithDeviceEXT; + DeviceD3D *mEGLDevice; HLSLCompiler mCompiler; + egl::Error initializeD3DDevice(); void initializeDevice(); void releaseDeviceResources(); void release(); + d3d11::ANGLED3D11DeviceType getDeviceType() const; + RenderStateCache mStateCache; // current render target states uintptr_t mAppliedRTVs[gl::IMPLEMENTATION_MAX_DRAW_BUFFERS]; uintptr_t mAppliedDSV; - bool mDepthStencilInitialized; - bool mRenderTargetDescInitialized; - unsigned int mCurStencilSize; - - struct RenderTargetDesc - { - size_t width; - size_t height; - DXGI_FORMAT format; - }; - RenderTargetDesc mRenderTargetDesc; // Currently applied sampler states std::vector mForceSetVertexSamplerStates; @@ -368,72 +367,8 @@ class Renderer11 : public RendererD3D std::vector mForceSetPixelSamplerStates; std::vector mCurPixelSamplerStates; - // Currently applied textures - struct SRVRecord - { - uintptr_t srv; - uintptr_t resource; - D3D11_SHADER_RESOURCE_VIEW_DESC desc; - }; - - // A cache of current SRVs that also tracks the highest 'used' (non-NULL) SRV - // We might want to investigate a more robust approach that is also fast when there's - // a large gap between used SRVs (e.g. if SRV 0 and 7 are non-NULL, this approach will - // waste time on SRVs 1-6.) - class SRVCache : angle::NonCopyable - { - public: - SRVCache() - : mHighestUsedSRV(0) - { - } - - void initialize(size_t size) - { - mCurrentSRVs.resize(size); - } - - size_t size() const { return mCurrentSRVs.size(); } - size_t highestUsed() const { return mHighestUsedSRV; } - - const SRVRecord &operator[](size_t index) const { return mCurrentSRVs[index]; } - void clear(); - void update(size_t resourceIndex, ID3D11ShaderResourceView *srv); - - private: - std::vector mCurrentSRVs; - size_t mHighestUsedSRV; - }; - - SRVCache mCurVertexSRVs; - SRVCache mCurPixelSRVs; - - // A block of NULL pointers, cached so we don't re-allocate every draw call - std::vector mNullSRVs; - StateManager11 mStateManager; - // Currently applied rasterizer state - bool mForceSetRasterState; - gl::RasterizerState mCurRasterState; - - // Currently applied depth stencil state - bool mForceSetDepthStencilState; - gl::DepthStencilState mCurDepthStencilState; - int mCurStencilRef; - int mCurStencilBackRef; - - // Currently applied scissor rectangle - bool mForceSetScissor; - bool mScissorEnabled; - gl::Rectangle mCurScissor; - - // Currently applied viewport - bool mForceSetViewport; - gl::Rectangle mCurViewport; - float mCurNear; - float mCurFar; - // Currently applied primitive topology D3D11_PRIMITIVE_TOPOLOGY mCurrentPrimitiveTopology; @@ -459,16 +394,14 @@ class Renderer11 : public RendererD3D uintptr_t mAppliedGeometryShader; uintptr_t mAppliedPixelShader; - dx_VertexConstants mVertexConstants; - dx_VertexConstants mAppliedVertexConstants; + dx_VertexConstants11 mAppliedVertexConstants; ID3D11Buffer *mDriverConstantBufferVS; ID3D11Buffer *mCurrentVertexConstantBuffer; unsigned int mCurrentConstantBufferVS[gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS]; GLintptr mCurrentConstantBufferVSOffset[gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS]; GLsizeiptr mCurrentConstantBufferVSSize[gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS]; - dx_PixelConstants mPixelConstants; - dx_PixelConstants mAppliedPixelConstants; + dx_PixelConstants11 mAppliedPixelConstants; ID3D11Buffer *mDriverConstantBufferPS; ID3D11Buffer *mCurrentPixelConstantBuffer; unsigned int mCurrentConstantBufferPS[gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS]; @@ -514,6 +447,8 @@ class Renderer11 : public RendererD3D ID3D11Debug *mDebug; std::vector mScratchIndexDataBuffer; + + mutable Optional mSupportsShareHandles; }; } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp index b52073129e..aa34fd4de8 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp @@ -9,16 +9,146 @@ #include "libANGLE/renderer/d3d/d3d11/StateManager11.h" #include "common/BitSetIterator.h" +#include "common/utilities.h" +#include "libANGLE/renderer/d3d/d3d11/Framebuffer11.h" #include "libANGLE/renderer/d3d/d3d11/Renderer11.h" +#include "libANGLE/renderer/d3d/d3d11/RenderTarget11.h" namespace rx { -StateManager11::StateManager11(Renderer11 *renderer11) - : mBlendStateIsDirty(false), +namespace +{ +bool ImageIndexConflictsWithSRV(const gl::ImageIndex &index, D3D11_SHADER_RESOURCE_VIEW_DESC desc) +{ + unsigned mipLevel = index.mipIndex; + unsigned layerIndex = index.layerIndex; + GLenum type = index.type; + + switch (desc.ViewDimension) + { + case D3D11_SRV_DIMENSION_TEXTURE2D: + { + unsigned maxSrvMip = desc.Texture2D.MipLevels + desc.Texture2D.MostDetailedMip; + maxSrvMip = (desc.Texture2D.MipLevels == -1) ? INT_MAX : maxSrvMip; + + unsigned mipMin = index.mipIndex; + unsigned mipMax = (layerIndex == -1) ? INT_MAX : layerIndex; + + return type == GL_TEXTURE_2D && + gl::RangeUI(mipMin, mipMax) + .intersects(gl::RangeUI(desc.Texture2D.MostDetailedMip, maxSrvMip)); + } + + case D3D11_SRV_DIMENSION_TEXTURE2DARRAY: + { + unsigned maxSrvMip = + desc.Texture2DArray.MipLevels + desc.Texture2DArray.MostDetailedMip; + maxSrvMip = (desc.Texture2DArray.MipLevels == -1) ? INT_MAX : maxSrvMip; + + unsigned maxSlice = desc.Texture2DArray.FirstArraySlice + desc.Texture2DArray.ArraySize; + + // Cube maps can be mapped to Texture2DArray SRVs + return (type == GL_TEXTURE_2D_ARRAY || gl::IsCubeMapTextureTarget(type)) && + desc.Texture2DArray.MostDetailedMip <= mipLevel && mipLevel < maxSrvMip && + desc.Texture2DArray.FirstArraySlice <= layerIndex && layerIndex < maxSlice; + } + + case D3D11_SRV_DIMENSION_TEXTURECUBE: + { + unsigned maxSrvMip = desc.TextureCube.MipLevels + desc.TextureCube.MostDetailedMip; + maxSrvMip = (desc.TextureCube.MipLevels == -1) ? INT_MAX : maxSrvMip; + + return gl::IsCubeMapTextureTarget(type) && + desc.TextureCube.MostDetailedMip <= mipLevel && mipLevel < maxSrvMip; + } + + case D3D11_SRV_DIMENSION_TEXTURE3D: + { + unsigned maxSrvMip = desc.Texture3D.MipLevels + desc.Texture3D.MostDetailedMip; + maxSrvMip = (desc.Texture3D.MipLevels == -1) ? INT_MAX : maxSrvMip; + + return type == GL_TEXTURE_3D && desc.Texture3D.MostDetailedMip <= mipLevel && + mipLevel < maxSrvMip; + } + default: + // We only handle the cases corresponding to valid image indexes + UNIMPLEMENTED(); + } + + return false; +} + +// Does *not* increment the resource ref count!! +ID3D11Resource *GetViewResource(ID3D11View *view) +{ + ID3D11Resource *resource = NULL; + ASSERT(view); + view->GetResource(&resource); + resource->Release(); + return resource; +} + +} // anonymous namespace + +void StateManager11::SRVCache::update(size_t resourceIndex, ID3D11ShaderResourceView *srv) +{ + ASSERT(resourceIndex < mCurrentSRVs.size()); + SRVRecord *record = &mCurrentSRVs[resourceIndex]; + + record->srv = reinterpret_cast(srv); + if (srv) + { + record->resource = reinterpret_cast(GetViewResource(srv)); + srv->GetDesc(&record->desc); + mHighestUsedSRV = std::max(resourceIndex + 1, mHighestUsedSRV); + } + else + { + record->resource = 0; + + if (resourceIndex + 1 == mHighestUsedSRV) + { + do + { + --mHighestUsedSRV; + } while (mHighestUsedSRV > 0 && mCurrentSRVs[mHighestUsedSRV].srv == 0); + } + } +} + +void StateManager11::SRVCache::clear() +{ + if (mCurrentSRVs.empty()) + { + return; + } + + memset(&mCurrentSRVs[0], 0, sizeof(SRVRecord) * mCurrentSRVs.size()); + mHighestUsedSRV = 0; +} + +StateManager11::StateManager11(Renderer11 *renderer) + : mRenderer(renderer), + mBlendStateIsDirty(false), mCurBlendColor(0, 0, 0, 0), mCurSampleMask(0), - mRenderer11(renderer11) + mDepthStencilStateIsDirty(false), + mCurStencilRef(0), + mCurStencilBackRef(0), + mCurStencilSize(0), + mRasterizerStateIsDirty(false), + mScissorStateIsDirty(false), + mCurScissorEnabled(false), + mCurScissorRect(), + mViewportStateIsDirty(false), + mCurViewport(), + mCurNear(0.0f), + mCurFar(0.0f), + mViewportBounds(), + mCurPresentPathFastEnabled(false), + mCurPresentPathFastColorBufferHeight(0), + mAppliedDSV(angle::DirtyPointer) { mCurBlendState.blend = false; mCurBlendState.sourceBlendRGB = GL_ONE; @@ -33,14 +163,82 @@ StateManager11::StateManager11(Renderer11 *renderer11) mCurBlendState.colorMaskAlpha = true; mCurBlendState.sampleAlphaToCoverage = false; mCurBlendState.dither = false; + + mCurDepthStencilState.depthTest = false; + mCurDepthStencilState.depthFunc = GL_LESS; + mCurDepthStencilState.depthMask = true; + mCurDepthStencilState.stencilTest = false; + mCurDepthStencilState.stencilMask = true; + mCurDepthStencilState.stencilFail = GL_KEEP; + mCurDepthStencilState.stencilPassDepthFail = GL_KEEP; + mCurDepthStencilState.stencilPassDepthPass = GL_KEEP; + mCurDepthStencilState.stencilWritemask = static_cast(-1); + mCurDepthStencilState.stencilBackFunc = GL_ALWAYS; + mCurDepthStencilState.stencilBackMask = static_cast(-1); + mCurDepthStencilState.stencilBackFail = GL_KEEP; + mCurDepthStencilState.stencilBackPassDepthFail = GL_KEEP; + mCurDepthStencilState.stencilBackPassDepthPass = GL_KEEP; + mCurDepthStencilState.stencilBackWritemask = static_cast(-1); + + mCurRasterState.rasterizerDiscard = false; + mCurRasterState.cullFace = false; + mCurRasterState.cullMode = GL_BACK; + mCurRasterState.frontFace = GL_CCW; + mCurRasterState.polygonOffsetFill = false; + mCurRasterState.polygonOffsetFactor = 0.0f; + mCurRasterState.polygonOffsetUnits = 0.0f; + mCurRasterState.pointDrawMode = false; + mCurRasterState.multiSample = false; } StateManager11::~StateManager11() { } +void StateManager11::updateStencilSizeIfChanged(bool depthStencilInitialized, + unsigned int stencilSize) +{ + if (!depthStencilInitialized || stencilSize != mCurStencilSize) + { + mCurStencilSize = stencilSize; + mDepthStencilStateIsDirty = true; + } +} + +void StateManager11::setViewportBounds(const int width, const int height) +{ + if (mRenderer->getRenderer11DeviceCaps().featureLevel <= D3D_FEATURE_LEVEL_9_3 && + (mViewportBounds.width != width || mViewportBounds.height != height)) + { + mViewportBounds = gl::Extents(width, height, 1); + mViewportStateIsDirty = true; + } +} + +void StateManager11::updatePresentPath(bool presentPathFastActive, + const gl::FramebufferAttachment *framebufferAttachment) +{ + const int colorBufferHeight = + framebufferAttachment ? framebufferAttachment->getSize().height : 0; + + if ((mCurPresentPathFastEnabled != presentPathFastActive) || + (presentPathFastActive && (colorBufferHeight != mCurPresentPathFastColorBufferHeight))) + { + mCurPresentPathFastEnabled = presentPathFastActive; + mCurPresentPathFastColorBufferHeight = colorBufferHeight; + mViewportStateIsDirty = true; // Viewport may need to be vertically inverted + mScissorStateIsDirty = true; // Scissor rect may need to be vertically inverted + mRasterizerStateIsDirty = true; // Cull Mode may need to be inverted + } +} + void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits) { + if (!dirtyBits.any()) + { + return; + } + for (unsigned int dirtyBit : angle::IterateBitSet(dirtyBits)) { switch (dirtyBit) @@ -104,6 +302,159 @@ void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBit mBlendStateIsDirty = true; } break; + case gl::State::DIRTY_BIT_DEPTH_MASK: + if (state.getDepthStencilState().depthMask != mCurDepthStencilState.depthMask) + { + mDepthStencilStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_DEPTH_TEST_ENABLED: + if (state.getDepthStencilState().depthTest != mCurDepthStencilState.depthTest) + { + mDepthStencilStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_DEPTH_FUNC: + if (state.getDepthStencilState().depthFunc != mCurDepthStencilState.depthFunc) + { + mDepthStencilStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_STENCIL_TEST_ENABLED: + if (state.getDepthStencilState().stencilTest != mCurDepthStencilState.stencilTest) + { + mDepthStencilStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_STENCIL_FUNCS_FRONT: + { + const gl::DepthStencilState &depthStencil = state.getDepthStencilState(); + if (depthStencil.stencilFunc != mCurDepthStencilState.stencilFunc || + depthStencil.stencilMask != mCurDepthStencilState.stencilMask || + state.getStencilRef() != mCurStencilRef) + { + mDepthStencilStateIsDirty = true; + } + break; + } + case gl::State::DIRTY_BIT_STENCIL_FUNCS_BACK: + { + const gl::DepthStencilState &depthStencil = state.getDepthStencilState(); + if (depthStencil.stencilBackFunc != mCurDepthStencilState.stencilBackFunc || + depthStencil.stencilBackMask != mCurDepthStencilState.stencilBackMask || + state.getStencilBackRef() != mCurStencilBackRef) + { + mDepthStencilStateIsDirty = true; + } + break; + } + case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_FRONT: + if (state.getDepthStencilState().stencilWritemask != + mCurDepthStencilState.stencilWritemask) + { + mDepthStencilStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_BACK: + if (state.getDepthStencilState().stencilBackWritemask != + mCurDepthStencilState.stencilBackWritemask) + { + mDepthStencilStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_STENCIL_OPS_FRONT: + { + const gl::DepthStencilState &depthStencil = state.getDepthStencilState(); + if (depthStencil.stencilFail != mCurDepthStencilState.stencilFail || + depthStencil.stencilPassDepthFail != + mCurDepthStencilState.stencilPassDepthFail || + depthStencil.stencilPassDepthPass != mCurDepthStencilState.stencilPassDepthPass) + { + mDepthStencilStateIsDirty = true; + } + break; + } + case gl::State::DIRTY_BIT_STENCIL_OPS_BACK: + { + const gl::DepthStencilState &depthStencil = state.getDepthStencilState(); + if (depthStencil.stencilBackFail != mCurDepthStencilState.stencilBackFail || + depthStencil.stencilBackPassDepthFail != + mCurDepthStencilState.stencilBackPassDepthFail || + depthStencil.stencilBackPassDepthPass != + mCurDepthStencilState.stencilBackPassDepthPass) + { + mDepthStencilStateIsDirty = true; + } + break; + } + case gl::State::DIRTY_BIT_CULL_FACE_ENABLED: + if (state.getRasterizerState().cullFace != mCurRasterState.cullFace) + { + mRasterizerStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_CULL_FACE: + if (state.getRasterizerState().cullMode != mCurRasterState.cullMode) + { + mRasterizerStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_FRONT_FACE: + if (state.getRasterizerState().frontFace != mCurRasterState.frontFace) + { + mRasterizerStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED: + if (state.getRasterizerState().polygonOffsetFill != + mCurRasterState.polygonOffsetFill) + { + mRasterizerStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_POLYGON_OFFSET: + { + const gl::RasterizerState &rasterState = state.getRasterizerState(); + if (rasterState.polygonOffsetFactor != mCurRasterState.polygonOffsetFactor || + rasterState.polygonOffsetUnits != mCurRasterState.polygonOffsetUnits) + { + mRasterizerStateIsDirty = true; + } + break; + } + case gl::State::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED: + if (state.getRasterizerState().rasterizerDiscard != + mCurRasterState.rasterizerDiscard) + { + mRasterizerStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_SCISSOR: + if (state.getScissor() != mCurScissorRect) + { + mScissorStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_SCISSOR_TEST_ENABLED: + if (state.isScissorTestEnabled() != mCurScissorEnabled) + { + mScissorStateIsDirty = true; + // Rasterizer state update needs mCurScissorsEnabled and updates when it changes + mRasterizerStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_DEPTH_RANGE: + if (state.getNearPlane() != mCurNear || state.getFarPlane() != mCurFar) + { + mViewportStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_VIEWPORT: + if (state.getViewport() != mCurViewport) + { + mViewportStateIsDirty = true; + } + break; default: break; } @@ -115,44 +466,572 @@ gl::Error StateManager11::setBlendState(const gl::Framebuffer *framebuffer, const gl::ColorF &blendColor, unsigned int sampleMask) { - if (mBlendStateIsDirty || sampleMask != mCurSampleMask) + if (!mBlendStateIsDirty && sampleMask == mCurSampleMask) { - ID3D11BlendState *dxBlendState = nullptr; - gl::Error error = - mRenderer11->getStateCache().getBlendState(framebuffer, blendState, &dxBlendState); + return gl::Error(GL_NO_ERROR); + } + + ID3D11BlendState *dxBlendState = nullptr; + gl::Error error = + mRenderer->getStateCache().getBlendState(framebuffer, blendState, &dxBlendState); + if (error.isError()) + { + return error; + } + + ASSERT(dxBlendState != nullptr); + + float blendColors[4] = {0.0f}; + if (blendState.sourceBlendRGB != GL_CONSTANT_ALPHA && + blendState.sourceBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA && + blendState.destBlendRGB != GL_CONSTANT_ALPHA && + blendState.destBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA) + { + blendColors[0] = blendColor.red; + blendColors[1] = blendColor.green; + blendColors[2] = blendColor.blue; + blendColors[3] = blendColor.alpha; + } + else + { + blendColors[0] = blendColor.alpha; + blendColors[1] = blendColor.alpha; + blendColors[2] = blendColor.alpha; + blendColors[3] = blendColor.alpha; + } + + mRenderer->getDeviceContext()->OMSetBlendState(dxBlendState, blendColors, sampleMask); + + mCurBlendState = blendState; + mCurBlendColor = blendColor; + mCurSampleMask = sampleMask; + + mBlendStateIsDirty = false; + + return error; +} + +gl::Error StateManager11::setDepthStencilState(const gl::State &glState) +{ + const auto &fbo = *glState.getDrawFramebuffer(); + + // Disable the depth test/depth write if we are using a stencil-only attachment. + // This is because ANGLE emulates stencil-only with D24S8 on D3D11 - we should neither read + // nor write to the unused depth part of this emulated texture. + bool disableDepth = (!fbo.hasDepth() && fbo.hasStencil()); + + // Similarly we disable the stencil portion of the DS attachment if the app only binds depth. + bool disableStencil = (fbo.hasDepth() && !fbo.hasStencil()); + + // CurDisableDepth/Stencil are reset automatically after we call forceSetDepthStencilState. + if (!mDepthStencilStateIsDirty && mCurDisableDepth.valid() && + disableDepth == mCurDisableDepth.value() && mCurDisableStencil.valid() && + disableStencil == mCurDisableStencil.value()) + { + return gl::Error(GL_NO_ERROR); + } + + const auto &depthStencilState = glState.getDepthStencilState(); + int stencilRef = glState.getStencilRef(); + int stencilBackRef = glState.getStencilBackRef(); + + // get the maximum size of the stencil ref + unsigned int maxStencil = 0; + if (depthStencilState.stencilTest && mCurStencilSize > 0) + { + maxStencil = (1 << mCurStencilSize) - 1; + } + ASSERT((depthStencilState.stencilWritemask & maxStencil) == + (depthStencilState.stencilBackWritemask & maxStencil)); + ASSERT(stencilRef == stencilBackRef); + ASSERT((depthStencilState.stencilMask & maxStencil) == + (depthStencilState.stencilBackMask & maxStencil)); + + ID3D11DepthStencilState *dxDepthStencilState = NULL; + gl::Error error = mRenderer->getStateCache().getDepthStencilState( + depthStencilState, disableDepth, disableStencil, &dxDepthStencilState); + if (error.isError()) + { + return error; + } + + ASSERT(dxDepthStencilState); + + // Max D3D11 stencil reference value is 0xFF, + // corresponding to the max 8 bits in a stencil buffer + // GL specifies we should clamp the ref value to the + // nearest bit depth when doing stencil ops + static_assert(D3D11_DEFAULT_STENCIL_READ_MASK == 0xFF, + "Unexpected value of D3D11_DEFAULT_STENCIL_READ_MASK"); + static_assert(D3D11_DEFAULT_STENCIL_WRITE_MASK == 0xFF, + "Unexpected value of D3D11_DEFAULT_STENCIL_WRITE_MASK"); + UINT dxStencilRef = std::min(stencilRef, 0xFFu); + + mRenderer->getDeviceContext()->OMSetDepthStencilState(dxDepthStencilState, dxStencilRef); + + mCurDepthStencilState = depthStencilState; + mCurStencilRef = stencilRef; + mCurStencilBackRef = stencilBackRef; + mCurDisableDepth = disableDepth; + mCurDisableStencil = disableStencil; + + mDepthStencilStateIsDirty = false; + + return gl::Error(GL_NO_ERROR); +} + +gl::Error StateManager11::setRasterizerState(const gl::RasterizerState &rasterState) +{ + if (!mRasterizerStateIsDirty) + { + return gl::Error(GL_NO_ERROR); + } + + ID3D11RasterizerState *dxRasterState = nullptr; + gl::Error error(GL_NO_ERROR); + + if (mCurPresentPathFastEnabled) + { + gl::RasterizerState modifiedRasterState = rasterState; + + // If prseent path fast is active then we need invert the front face state. + // This ensures that both gl_FrontFacing is correct, and front/back culling + // is performed correctly. + if (modifiedRasterState.frontFace == GL_CCW) + { + modifiedRasterState.frontFace = GL_CW; + } + else + { + ASSERT(modifiedRasterState.frontFace == GL_CW); + modifiedRasterState.frontFace = GL_CCW; + } + + error = mRenderer->getStateCache().getRasterizerState(modifiedRasterState, + mCurScissorEnabled, &dxRasterState); + } + else + { + error = mRenderer->getStateCache().getRasterizerState(rasterState, mCurScissorEnabled, + &dxRasterState); + } + + if (error.isError()) + { + return error; + } + + mRenderer->getDeviceContext()->RSSetState(dxRasterState); + + mCurRasterState = rasterState; + mRasterizerStateIsDirty = false; + + return error; +} + +void StateManager11::setScissorRectangle(const gl::Rectangle &scissor, bool enabled) +{ + if (!mScissorStateIsDirty) + return; + + int modifiedScissorY = scissor.y; + if (mCurPresentPathFastEnabled) + { + modifiedScissorY = mCurPresentPathFastColorBufferHeight - scissor.height - scissor.y; + } + + if (enabled) + { + D3D11_RECT rect; + rect.left = std::max(0, scissor.x); + rect.top = std::max(0, modifiedScissorY); + rect.right = scissor.x + std::max(0, scissor.width); + rect.bottom = modifiedScissorY + std::max(0, scissor.height); + + mRenderer->getDeviceContext()->RSSetScissorRects(1, &rect); + } + + mCurScissorRect = scissor; + mCurScissorEnabled = enabled; + mScissorStateIsDirty = false; +} + +void StateManager11::setViewport(const gl::Caps *caps, + const gl::Rectangle &viewport, + float zNear, + float zFar) +{ + if (!mViewportStateIsDirty) + return; + + float actualZNear = gl::clamp01(zNear); + float actualZFar = gl::clamp01(zFar); + + int dxMaxViewportBoundsX = static_cast(caps->maxViewportWidth); + int dxMaxViewportBoundsY = static_cast(caps->maxViewportHeight); + int dxMinViewportBoundsX = -dxMaxViewportBoundsX; + int dxMinViewportBoundsY = -dxMaxViewportBoundsY; + + if (mRenderer->getRenderer11DeviceCaps().featureLevel <= D3D_FEATURE_LEVEL_9_3) + { + // Feature Level 9 viewports shouldn't exceed the dimensions of the rendertarget. + dxMaxViewportBoundsX = static_cast(mViewportBounds.width); + dxMaxViewportBoundsY = static_cast(mViewportBounds.height); + dxMinViewportBoundsX = 0; + dxMinViewportBoundsY = 0; + } + + int dxViewportTopLeftX = gl::clamp(viewport.x, dxMinViewportBoundsX, dxMaxViewportBoundsX); + int dxViewportTopLeftY = gl::clamp(viewport.y, dxMinViewportBoundsY, dxMaxViewportBoundsY); + int dxViewportWidth = gl::clamp(viewport.width, 0, dxMaxViewportBoundsX - dxViewportTopLeftX); + int dxViewportHeight = gl::clamp(viewport.height, 0, dxMaxViewportBoundsY - dxViewportTopLeftY); + + D3D11_VIEWPORT dxViewport; + dxViewport.TopLeftX = static_cast(dxViewportTopLeftX); + + if (mCurPresentPathFastEnabled) + { + // When present path fast is active and we're rendering to framebuffer 0, we must invert + // the viewport in Y-axis. + // NOTE: We delay the inversion until right before the call to RSSetViewports, and leave + // dxViewportTopLeftY unchanged. This allows us to calculate viewAdjust below using the + // unaltered dxViewportTopLeftY value. + dxViewport.TopLeftY = static_cast(mCurPresentPathFastColorBufferHeight - + dxViewportTopLeftY - dxViewportHeight); + } + else + { + dxViewport.TopLeftY = static_cast(dxViewportTopLeftY); + } + + dxViewport.Width = static_cast(dxViewportWidth); + dxViewport.Height = static_cast(dxViewportHeight); + dxViewport.MinDepth = actualZNear; + dxViewport.MaxDepth = actualZFar; + + mRenderer->getDeviceContext()->RSSetViewports(1, &dxViewport); + + mCurViewport = viewport; + mCurNear = actualZNear; + mCurFar = actualZFar; + + // On Feature Level 9_*, we must emulate large and/or negative viewports in the shaders + // using viewAdjust (like the D3D9 renderer). + if (mRenderer->getRenderer11DeviceCaps().featureLevel <= D3D_FEATURE_LEVEL_9_3) + { + mVertexConstants.viewAdjust[0] = static_cast((viewport.width - dxViewportWidth) + + 2 * (viewport.x - dxViewportTopLeftX)) / + dxViewport.Width; + mVertexConstants.viewAdjust[1] = static_cast((viewport.height - dxViewportHeight) + + 2 * (viewport.y - dxViewportTopLeftY)) / + dxViewport.Height; + mVertexConstants.viewAdjust[2] = static_cast(viewport.width) / dxViewport.Width; + mVertexConstants.viewAdjust[3] = static_cast(viewport.height) / dxViewport.Height; + } + + mPixelConstants.viewCoords[0] = viewport.width * 0.5f; + mPixelConstants.viewCoords[1] = viewport.height * 0.5f; + mPixelConstants.viewCoords[2] = viewport.x + (viewport.width * 0.5f); + mPixelConstants.viewCoords[3] = viewport.y + (viewport.height * 0.5f); + + // Instanced pointsprite emulation requires ViewCoords to be defined in the + // the vertex shader. + mVertexConstants.viewCoords[0] = mPixelConstants.viewCoords[0]; + mVertexConstants.viewCoords[1] = mPixelConstants.viewCoords[1]; + mVertexConstants.viewCoords[2] = mPixelConstants.viewCoords[2]; + mVertexConstants.viewCoords[3] = mPixelConstants.viewCoords[3]; + + mPixelConstants.depthFront[0] = (actualZFar - actualZNear) * 0.5f; + mPixelConstants.depthFront[1] = (actualZNear + actualZFar) * 0.5f; + + mVertexConstants.depthRange[0] = actualZNear; + mVertexConstants.depthRange[1] = actualZFar; + mVertexConstants.depthRange[2] = actualZFar - actualZNear; + + mPixelConstants.depthRange[0] = actualZNear; + mPixelConstants.depthRange[1] = actualZFar; + mPixelConstants.depthRange[2] = actualZFar - actualZNear; + + mPixelConstants.viewScale[0] = 1.0f; + mPixelConstants.viewScale[1] = mCurPresentPathFastEnabled ? 1.0f : -1.0f; + mPixelConstants.viewScale[2] = 1.0f; + mPixelConstants.viewScale[3] = 1.0f; + + mVertexConstants.viewScale[0] = mPixelConstants.viewScale[0]; + mVertexConstants.viewScale[1] = mPixelConstants.viewScale[1]; + mVertexConstants.viewScale[2] = mPixelConstants.viewScale[2]; + mVertexConstants.viewScale[3] = mPixelConstants.viewScale[3]; + + mViewportStateIsDirty = false; +} + +void StateManager11::invalidateRenderTarget() +{ + for (auto &appliedRTV : mAppliedRTVs) + { + appliedRTV = angle::DirtyPointer; + } + mAppliedDSV = angle::DirtyPointer; +} + +void StateManager11::invalidateEverything() +{ + mBlendStateIsDirty = true; + mDepthStencilStateIsDirty = true; + mRasterizerStateIsDirty = true; + mScissorStateIsDirty = true; + mViewportStateIsDirty = true; + + // We reset the current SRV data because it might not be in sync with D3D's state + // anymore. For example when a currently used SRV is used as an RTV, D3D silently + // remove it from its state. + mCurVertexSRVs.clear(); + mCurPixelSRVs.clear(); + + invalidateRenderTarget(); +} + +bool StateManager11::setRenderTargets(const RenderTargetArray &renderTargets, + ID3D11DepthStencilView *depthStencil) +{ + // TODO(jmadill): Use context caps? + UINT drawBuffers = mRenderer->getRendererCaps().maxDrawBuffers; + + // Apply the render target and depth stencil + size_t arraySize = sizeof(uintptr_t) * drawBuffers; + if (memcmp(renderTargets.data(), mAppliedRTVs.data(), arraySize) == 0 && + reinterpret_cast(depthStencil) == mAppliedDSV) + { + return false; + } + + // The D3D11 blend state is heavily dependent on the current render target. + mBlendStateIsDirty = true; + + for (UINT rtIndex = 0; rtIndex < drawBuffers; rtIndex++) + { + mAppliedRTVs[rtIndex] = reinterpret_cast(renderTargets[rtIndex]); + } + mAppliedDSV = reinterpret_cast(depthStencil); + + mRenderer->getDeviceContext()->OMSetRenderTargets(drawBuffers, renderTargets.data(), + depthStencil); + return true; +} + +void StateManager11::setRenderTarget(ID3D11RenderTargetView *renderTarget, + ID3D11DepthStencilView *depthStencil) +{ + mRenderer->getDeviceContext()->OMSetRenderTargets(1, &renderTarget, depthStencil); +} + +void StateManager11::setShaderResource(gl::SamplerType shaderType, + UINT resourceSlot, + ID3D11ShaderResourceView *srv) +{ + auto ¤tSRVs = (shaderType == gl::SAMPLER_VERTEX ? mCurVertexSRVs : mCurPixelSRVs); + + ASSERT(static_cast(resourceSlot) < currentSRVs.size()); + const SRVRecord &record = currentSRVs[resourceSlot]; + + if (record.srv != reinterpret_cast(srv)) + { + auto deviceContext = mRenderer->getDeviceContext(); + if (shaderType == gl::SAMPLER_VERTEX) + { + deviceContext->VSSetShaderResources(resourceSlot, 1, &srv); + } + else + { + deviceContext->PSSetShaderResources(resourceSlot, 1, &srv); + } + + currentSRVs.update(resourceSlot, srv); + } +} + +gl::Error StateManager11::clearTextures(gl::SamplerType samplerType, + size_t rangeStart, + size_t rangeEnd) +{ + if (rangeStart == rangeEnd) + { + return gl::Error(GL_NO_ERROR); + } + + auto ¤tSRVs = (samplerType == gl::SAMPLER_VERTEX ? mCurVertexSRVs : mCurPixelSRVs); + + gl::Range clearRange(rangeStart, rangeStart); + clearRange.extend(std::min(rangeEnd, currentSRVs.highestUsed())); + + if (clearRange.empty()) + { + return gl::Error(GL_NO_ERROR); + } + + auto deviceContext = mRenderer->getDeviceContext(); + if (samplerType == gl::SAMPLER_VERTEX) + { + deviceContext->VSSetShaderResources(static_cast(rangeStart), + static_cast(rangeEnd - rangeStart), + &mNullSRVs[0]); + } + else + { + deviceContext->PSSetShaderResources(static_cast(rangeStart), + static_cast(rangeEnd - rangeStart), + &mNullSRVs[0]); + } + + for (size_t samplerIndex = rangeStart; samplerIndex < rangeEnd; ++samplerIndex) + { + currentSRVs.update(samplerIndex, nullptr); + } + + return gl::Error(GL_NO_ERROR); +} + +void StateManager11::unsetConflictingSRVs(gl::SamplerType samplerType, + uintptr_t resource, + const gl::ImageIndex &index) +{ + auto ¤tSRVs = (samplerType == gl::SAMPLER_VERTEX ? mCurVertexSRVs : mCurPixelSRVs); + + for (size_t resourceIndex = 0; resourceIndex < currentSRVs.size(); ++resourceIndex) + { + auto &record = currentSRVs[resourceIndex]; + + if (record.srv && record.resource == resource && + ImageIndexConflictsWithSRV(index, record.desc)) + { + setShaderResource(samplerType, static_cast(resourceIndex), NULL); + } + } +} + +void StateManager11::initialize(const gl::Caps &caps) +{ + mCurVertexSRVs.initialize(caps.maxVertexTextureImageUnits); + mCurPixelSRVs.initialize(caps.maxTextureImageUnits); + + // Initialize cached NULL SRV block + mNullSRVs.resize(caps.maxTextureImageUnits, nullptr); +} + +gl::Error StateManager11::syncFramebuffer(const gl::Framebuffer *framebuffer) +{ + // Get the color render buffer and serial + // Also extract the render target dimensions and view + unsigned int renderTargetWidth = 0; + unsigned int renderTargetHeight = 0; + DXGI_FORMAT renderTargetFormat = DXGI_FORMAT_UNKNOWN; + RenderTargetArray framebufferRTVs; + bool missingColorRenderTarget = true; + + framebufferRTVs.fill(nullptr); + + const Framebuffer11 *framebuffer11 = GetImplAs(framebuffer); + const gl::AttachmentList &colorbuffers = framebuffer11->getColorAttachmentsForRender(); + + for (size_t colorAttachment = 0; colorAttachment < colorbuffers.size(); ++colorAttachment) + { + const gl::FramebufferAttachment *colorbuffer = colorbuffers[colorAttachment]; + + if (colorbuffer) + { + // the draw buffer must be either "none", "back" for the default buffer or the same + // index as this color (in order) + + // check for zero-sized default framebuffer, which is a special case. + // in this case we do not wish to modify any state and just silently return false. + // this will not report any gl error but will cause the calling method to return. + const gl::Extents &size = colorbuffer->getSize(); + if (size.width == 0 || size.height == 0) + { + return gl::Error(GL_NO_ERROR); + } + + // Extract the render target dimensions and view + RenderTarget11 *renderTarget = NULL; + gl::Error error = colorbuffer->getRenderTarget(&renderTarget); + if (error.isError()) + { + return error; + } + ASSERT(renderTarget); + + framebufferRTVs[colorAttachment] = renderTarget->getRenderTargetView(); + ASSERT(framebufferRTVs[colorAttachment]); + + if (missingColorRenderTarget) + { + renderTargetWidth = renderTarget->getWidth(); + renderTargetHeight = renderTarget->getHeight(); + renderTargetFormat = renderTarget->getDXGIFormat(); + missingColorRenderTarget = false; + } + + // Unbind render target SRVs from the shader here to prevent D3D11 warnings. + if (colorbuffer->type() == GL_TEXTURE) + { + uintptr_t rtResource = + reinterpret_cast(GetViewResource(framebufferRTVs[colorAttachment])); + const gl::ImageIndex &index = colorbuffer->getTextureImageIndex(); + // The index doesn't need to be corrected for the small compressed texture + // workaround + // because a rendertarget is never compressed. + unsetConflictingSRVs(gl::SAMPLER_VERTEX, rtResource, index); + unsetConflictingSRVs(gl::SAMPLER_PIXEL, rtResource, index); + } + } + } + + // Get the depth stencil buffers + ID3D11DepthStencilView *framebufferDSV = NULL; + const gl::FramebufferAttachment *depthStencil = framebuffer->getDepthOrStencilbuffer(); + if (depthStencil) + { + RenderTarget11 *depthStencilRenderTarget = NULL; + gl::Error error = depthStencil->getRenderTarget(&depthStencilRenderTarget); if (error.isError()) { return error; } + ASSERT(depthStencilRenderTarget); - ASSERT(dxBlendState != nullptr); + framebufferDSV = depthStencilRenderTarget->getDepthStencilView(); + ASSERT(framebufferDSV); - float blendColors[4] = {0.0f}; - if (blendState.sourceBlendRGB != GL_CONSTANT_ALPHA && - blendState.sourceBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA && - blendState.destBlendRGB != GL_CONSTANT_ALPHA && - blendState.destBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA) + // If there is no render buffer, the width, height and format values come from + // the depth stencil + if (missingColorRenderTarget) { - blendColors[0] = blendColor.red; - blendColors[1] = blendColor.green; - blendColors[2] = blendColor.blue; - blendColors[3] = blendColor.alpha; - } - else - { - blendColors[0] = blendColor.alpha; - blendColors[1] = blendColor.alpha; - blendColors[2] = blendColor.alpha; - blendColors[3] = blendColor.alpha; + renderTargetWidth = depthStencilRenderTarget->getWidth(); + renderTargetHeight = depthStencilRenderTarget->getHeight(); } - mRenderer11->getDeviceContext()->OMSetBlendState(dxBlendState, blendColors, sampleMask); + // Unbind render target SRVs from the shader here to prevent D3D11 warnings. + if (depthStencil->type() == GL_TEXTURE) + { + uintptr_t depthStencilResource = + reinterpret_cast(GetViewResource(framebufferDSV)); + const gl::ImageIndex &index = depthStencil->getTextureImageIndex(); + // The index doesn't need to be corrected for the small compressed texture workaround + // because a rendertarget is never compressed. + unsetConflictingSRVs(gl::SAMPLER_VERTEX, depthStencilResource, index); + unsetConflictingSRVs(gl::SAMPLER_PIXEL, depthStencilResource, index); + } + } - mCurBlendState = blendState; - mCurBlendColor = blendColor; - mCurSampleMask = sampleMask; + if (setRenderTargets(framebufferRTVs, framebufferDSV)) + { + setViewportBounds(renderTargetWidth, renderTargetHeight); + } - mBlendStateIsDirty = false; + gl::Error error = framebuffer11->invalidateSwizzles(); + if (error.isError()) + { + return error; } return gl::Error(GL_NO_ERROR); diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.h index 6738972616..f900882d7e 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.h @@ -9,23 +9,44 @@ #ifndef LIBANGLE_RENDERER_D3D11_STATEMANAGER11_H_ #define LIBANGLE_RENDERER_D3D11_STATEMANAGER11_H_ +#include + #include "libANGLE/angletypes.h" #include "libANGLE/Data.h" #include "libANGLE/State.h" #include "libANGLE/renderer/d3d/d3d11/RenderStateCache.h" +#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h" +#include "libANGLE/renderer/d3d/RendererD3D.h" namespace rx { -class Renderer11; +struct RenderTargetDesc; +struct Renderer11DeviceCaps; + +struct dx_VertexConstants11 +{ + float depthRange[4]; + float viewAdjust[4]; + float viewCoords[4]; + float viewScale[4]; +}; + +struct dx_PixelConstants11 +{ + float depthRange[4]; + float viewCoords[4]; + float depthFront[4]; + float viewScale[4]; +}; class StateManager11 final : angle::NonCopyable { public: - StateManager11(Renderer11 *renderer11); - + StateManager11(Renderer11 *renderer); ~StateManager11(); + void initialize(const gl::Caps &caps); void syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits); gl::Error setBlendState(const gl::Framebuffer *framebuffer, @@ -33,9 +54,44 @@ class StateManager11 final : angle::NonCopyable const gl::ColorF &blendColor, unsigned int sampleMask); - void forceSetBlendState() { mBlendStateIsDirty = true; } + gl::Error setDepthStencilState(const gl::State &glState); + + gl::Error setRasterizerState(const gl::RasterizerState &rasterState); + + void setScissorRectangle(const gl::Rectangle &scissor, bool enabled); + + void setViewport(const gl::Caps *caps, const gl::Rectangle &viewport, float zNear, float zFar); + + void updatePresentPath(bool presentPathFastActive, + const gl::FramebufferAttachment *framebufferAttachment); + + const dx_VertexConstants11 &getVertexConstants() const { return mVertexConstants; } + const dx_PixelConstants11 &getPixelConstants() const { return mPixelConstants; } + + void updateStencilSizeIfChanged(bool depthStencilInitialized, unsigned int stencilSize); + + void setShaderResource(gl::SamplerType shaderType, + UINT resourceSlot, + ID3D11ShaderResourceView *srv); + gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd); + + gl::Error syncFramebuffer(const gl::Framebuffer *framebuffer); + + void invalidateRenderTarget(); + void invalidateEverything(); + bool setRenderTargets(const RenderTargetArray &renderTargets, + ID3D11DepthStencilView *depthStencil); + void setRenderTarget(ID3D11RenderTargetView *renderTarget, + ID3D11DepthStencilView *depthStencil); private: + void unsetConflictingSRVs(gl::SamplerType shaderType, + uintptr_t resource, + const gl::ImageIndex &index); + void setViewportBounds(const int width, const int height); + + Renderer11 *mRenderer; + // Blend State bool mBlendStateIsDirty; // TODO(dianx) temporary representation of a dirty bit. once we move enough states in, @@ -44,8 +100,82 @@ class StateManager11 final : angle::NonCopyable gl::ColorF mCurBlendColor; unsigned int mCurSampleMask; - Renderer11 *mRenderer11; + // Currently applied depth stencil state + bool mDepthStencilStateIsDirty; + gl::DepthStencilState mCurDepthStencilState; + int mCurStencilRef; + int mCurStencilBackRef; + unsigned int mCurStencilSize; + Optional mCurDisableDepth; + Optional mCurDisableStencil; + + // Currently applied rasterizer state + bool mRasterizerStateIsDirty; + gl::RasterizerState mCurRasterState; + + // Currently applied scissor rectangle state + bool mScissorStateIsDirty; + bool mCurScissorEnabled; + gl::Rectangle mCurScissorRect; + + // Currently applied viewport state + bool mViewportStateIsDirty; + gl::Rectangle mCurViewport; + float mCurNear; + float mCurFar; + + // Things needed in viewport state + dx_VertexConstants11 mVertexConstants; + dx_PixelConstants11 mPixelConstants; + + // Render target variables + gl::Extents mViewportBounds; + + // EGL_ANGLE_experimental_present_path variables + bool mCurPresentPathFastEnabled; + int mCurPresentPathFastColorBufferHeight; + + // Current RenderTarget state + std::array mAppliedRTVs; + uintptr_t mAppliedDSV; + + // Currently applied textures + struct SRVRecord + { + uintptr_t srv; + uintptr_t resource; + D3D11_SHADER_RESOURCE_VIEW_DESC desc; + }; + + // A cache of current SRVs that also tracks the highest 'used' (non-NULL) SRV + // We might want to investigate a more robust approach that is also fast when there's + // a large gap between used SRVs (e.g. if SRV 0 and 7 are non-NULL, this approach will + // waste time on SRVs 1-6.) + class SRVCache : angle::NonCopyable + { + public: + SRVCache() : mHighestUsedSRV(0) {} + + void initialize(size_t size) { mCurrentSRVs.resize(size); } + + size_t size() const { return mCurrentSRVs.size(); } + size_t highestUsed() const { return mHighestUsedSRV; } + + const SRVRecord &operator[](size_t index) const { return mCurrentSRVs[index]; } + void clear(); + void update(size_t resourceIndex, ID3D11ShaderResourceView *srv); + + private: + std::vector mCurrentSRVs; + size_t mHighestUsedSRV; + }; + + SRVCache mCurVertexSRVs; + SRVCache mCurPixelSRVs; + + // A block of NULL pointers, cached so we don't re-allocate every draw call + std::vector mNullSRVs; }; } // namespace rx -#endif // LIBANGLE_RENDERER_D3D11_STATEMANAGER11_H_ \ No newline at end of file +#endif // LIBANGLE_RENDERER_D3D11_STATEMANAGER11_H_ diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp index 8a4b6199d1..a56d3fa6f7 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp @@ -8,6 +8,8 @@ #include "libANGLE/renderer/d3d/d3d11/SwapChain11.h" +#include + #include "libANGLE/features.h" #include "libANGLE/renderer/d3d/d3d11/formatutils11.h" #include "libANGLE/renderer/d3d/d3d11/NativeWindow.h" @@ -29,40 +31,56 @@ namespace rx { -SwapChain11::SwapChain11(Renderer11 *renderer, NativeWindow nativeWindow, HANDLE shareHandle, - GLenum backBufferFormat, GLenum depthBufferFormat) +namespace +{ +bool NeedsOffscreenTexture(Renderer11 *renderer, NativeWindow nativeWindow, EGLint orientation) +{ + // We don't need an offscreen texture if either orientation = INVERT_Y, + // or present path fast is enabled and we're not rendering onto an offscreen surface. + return orientation != EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE && + !(renderer->presentPathFastEnabled() && nativeWindow.getNativeWindow()); +} +} // anonymous namespace + +SwapChain11::SwapChain11(Renderer11 *renderer, + NativeWindow nativeWindow, + HANDLE shareHandle, + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation) : SwapChainD3D(nativeWindow, shareHandle, backBufferFormat, depthBufferFormat), mRenderer(renderer), + mWidth(-1), + mHeight(-1), + mOrientation(orientation), + mAppCreatedShareHandle(mShareHandle != nullptr), + mSwapInterval(0), mPassThroughResourcesInit(false), + mFirstSwap(true), + mSwapChain(nullptr), + mSwapChain1(nullptr), + mKeyedMutex(nullptr), + mBackBufferTexture(nullptr), + mBackBufferRTView(nullptr), + mBackBufferSRView(nullptr), + mNeedsOffscreenTexture(NeedsOffscreenTexture(renderer, nativeWindow, orientation)), + mOffscreenTexture(nullptr), + mOffscreenRTView(nullptr), + mOffscreenSRView(nullptr), + mDepthStencilTexture(nullptr), + mDepthStencilDSView(nullptr), + mDepthStencilSRView(nullptr), + mQuadVB(nullptr), + mPassThroughSampler(nullptr), + mPassThroughIL(nullptr), + mPassThroughVS(nullptr), + mPassThroughPS(nullptr), + mPassThroughRS(nullptr), mColorRenderTarget(this, renderer, false), mDepthStencilRenderTarget(this, renderer, true) { - mHeight = -1; - mWidth = -1; - mAppCreatedShareHandle = mShareHandle != NULL; - mSwapInterval = 0; - - mSwapChain = NULL; - mSwapChain1 = nullptr; - - mKeyedMutex = nullptr; - - mBackBufferTexture = NULL; - mBackBufferRTView = NULL; - - mOffscreenTexture = NULL; - mOffscreenRTView = NULL; - mOffscreenSRView = NULL; - - mDepthStencilTexture = NULL; - mDepthStencilDSView = NULL; - mDepthStencilSRView = NULL; - - mQuadVB = NULL; - mPassThroughSampler = NULL; - mPassThroughIL = NULL; - mPassThroughVS = NULL; - mPassThroughPS = NULL; + // Sanity check that if present path fast is active then we're using the default orientation + ASSERT(!mRenderer->presentPathFastEnabled() || orientation == 0); } SwapChain11::~SwapChain11() @@ -77,6 +95,7 @@ void SwapChain11::release() SafeRelease(mKeyedMutex); SafeRelease(mBackBufferTexture); SafeRelease(mBackBufferRTView); + SafeRelease(mBackBufferSRView); SafeRelease(mOffscreenTexture); SafeRelease(mOffscreenRTView); SafeRelease(mOffscreenSRView); @@ -88,6 +107,7 @@ void SwapChain11::release() SafeRelease(mPassThroughIL); SafeRelease(mPassThroughVS); SafeRelease(mPassThroughPS); + SafeRelease(mPassThroughRS); if (!mAppCreatedShareHandle) { @@ -95,18 +115,47 @@ void SwapChain11::release() } } -void SwapChain11::releaseOffscreenTexture() +void SwapChain11::releaseOffscreenColorBuffer() { SafeRelease(mOffscreenTexture); SafeRelease(mOffscreenRTView); SafeRelease(mOffscreenSRView); +} + +void SwapChain11::releaseOffscreenDepthBuffer() +{ SafeRelease(mDepthStencilTexture); SafeRelease(mDepthStencilDSView); SafeRelease(mDepthStencilSRView); } -EGLint SwapChain11::resetOffscreenTexture(int backbufferWidth, int backbufferHeight) +EGLint SwapChain11::resetOffscreenBuffers(int backbufferWidth, int backbufferHeight) { + if (mNeedsOffscreenTexture) + { + EGLint result = resetOffscreenColorBuffer(backbufferWidth, backbufferHeight); + if (result != EGL_SUCCESS) + { + return result; + } + } + + EGLint result = resetOffscreenDepthBuffer(backbufferWidth, backbufferHeight); + if (result != EGL_SUCCESS) + { + return result; + } + + mWidth = backbufferWidth; + mHeight = backbufferHeight; + + return EGL_SUCCESS; +} + +EGLint SwapChain11::resetOffscreenColorBuffer(int backbufferWidth, int backbufferHeight) +{ + ASSERT(mNeedsOffscreenTexture); + TRACE_EVENT0("gpu.angle", "SwapChain11::resetOffscreenTexture"); ID3D11Device *device = mRenderer->getDevice(); @@ -125,7 +174,7 @@ EGLint SwapChain11::resetOffscreenTexture(int backbufferWidth, int backbufferHei const int previousWidth = mWidth; const int previousHeight = mHeight; - releaseOffscreenTexture(); + releaseOffscreenColorBuffer(); const d3d11::TextureFormat &backbufferFormatInfo = d3d11::GetTextureFormatInfo(mOffscreenRenderTargetFormat, mRenderer->getRenderer11DeviceCaps()); @@ -227,9 +276,10 @@ EGLint SwapChain11::resetOffscreenTexture(int backbufferWidth, int backbufferHei } } } - mKeyedMutex = d3d11::DynamicCastComObject(mOffscreenTexture); } + // This may return null if the original texture was created without a keyed mutex. + mKeyedMutex = d3d11::DynamicCastComObject(mOffscreenTexture); D3D11_RENDER_TARGET_VIEW_DESC offscreenRTVDesc; offscreenRTVDesc.Format = backbufferFormatInfo.rtvFormat; @@ -250,10 +300,41 @@ EGLint SwapChain11::resetOffscreenTexture(int backbufferWidth, int backbufferHei ASSERT(SUCCEEDED(result)); d3d11::SetDebugName(mOffscreenSRView, "Offscreen back buffer shader resource"); - const d3d11::TextureFormat &depthBufferFormatInfo = d3d11::GetTextureFormatInfo(mDepthBufferFormat, mRenderer->getRenderer11DeviceCaps()); + if (previousOffscreenTexture != nullptr) + { + D3D11_BOX sourceBox = {0}; + sourceBox.left = 0; + sourceBox.right = std::min(previousWidth, backbufferWidth); + sourceBox.top = std::max(previousHeight - backbufferHeight, 0); + sourceBox.bottom = previousHeight; + sourceBox.front = 0; + sourceBox.back = 1; + + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + const int yoffset = std::max(backbufferHeight - previousHeight, 0); + deviceContext->CopySubresourceRegion(mOffscreenTexture, 0, 0, yoffset, 0, + previousOffscreenTexture, 0, &sourceBox); + + SafeRelease(previousOffscreenTexture); + + if (mSwapChain) + { + swapRect(0, 0, backbufferWidth, backbufferHeight); + } + } + + return EGL_SUCCESS; +} + +EGLint SwapChain11::resetOffscreenDepthBuffer(int backbufferWidth, int backbufferHeight) +{ + releaseOffscreenDepthBuffer(); if (mDepthBufferFormat != GL_NONE) { + const d3d11::TextureFormat &depthBufferFormatInfo = + d3d11::GetTextureFormatInfo(mDepthBufferFormat, mRenderer->getRenderer11DeviceCaps()); + D3D11_TEXTURE2D_DESC depthStencilTextureDesc; depthStencilTextureDesc.Width = backbufferWidth; depthStencilTextureDesc.Height = backbufferHeight; @@ -273,7 +354,9 @@ EGLint SwapChain11::resetOffscreenTexture(int backbufferWidth, int backbufferHei depthStencilTextureDesc.CPUAccessFlags = 0; depthStencilTextureDesc.MiscFlags = 0; - result = device->CreateTexture2D(&depthStencilTextureDesc, NULL, &mDepthStencilTexture); + ID3D11Device *device = mRenderer->getDevice(); + HRESULT result = + device->CreateTexture2D(&depthStencilTextureDesc, NULL, &mDepthStencilTexture); if (FAILED(result)) { ERR("Could not create depthstencil surface for new swap chain: 0x%08X", result); @@ -314,31 +397,6 @@ EGLint SwapChain11::resetOffscreenTexture(int backbufferWidth, int backbufferHei } } - mWidth = backbufferWidth; - mHeight = backbufferHeight; - - if (previousOffscreenTexture != NULL) - { - D3D11_BOX sourceBox = {0}; - sourceBox.left = 0; - sourceBox.right = std::min(previousWidth, mWidth); - sourceBox.top = std::max(previousHeight - mHeight, 0); - sourceBox.bottom = previousHeight; - sourceBox.front = 0; - sourceBox.back = 1; - - ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); - const int yoffset = std::max(mHeight - previousHeight, 0); - deviceContext->CopySubresourceRegion(mOffscreenTexture, 0, 0, yoffset, 0, previousOffscreenTexture, 0, &sourceBox); - - SafeRelease(previousOffscreenTexture); - - if (mSwapChain) - { - swapRect(0, 0, mWidth, mHeight); - } - } - return EGL_SUCCESS; } @@ -358,11 +416,18 @@ EGLint SwapChain11::resize(EGLint backbufferWidth, EGLint backbufferHeight) return EGL_SUCCESS; } + // Don't resize unnecessarily + if (mWidth == backbufferWidth && mHeight == backbufferHeight) + { + return EGL_SUCCESS; + } + // Can only call resize if we have already created our swap buffer and resources - ASSERT(mSwapChain && mBackBufferTexture && mBackBufferRTView); + ASSERT(mSwapChain && mBackBufferTexture && mBackBufferRTView && mBackBufferSRView); SafeRelease(mBackBufferTexture); SafeRelease(mBackBufferRTView); + SafeRelease(mBackBufferSRView); // Resize swap chain DXGI_SWAP_CHAIN_DESC desc; @@ -398,14 +463,22 @@ EGLint SwapChain11::resize(EGLint backbufferWidth, EGLint backbufferHeight) d3d11::SetDebugName(mBackBufferTexture, "Back buffer texture"); result = device->CreateRenderTargetView(mBackBufferTexture, NULL, &mBackBufferRTView); ASSERT(SUCCEEDED(result)); + if (SUCCEEDED(result)) + { + d3d11::SetDebugName(mBackBufferRTView, "Back buffer render target"); + } + + result = device->CreateShaderResourceView(mBackBufferTexture, nullptr, &mBackBufferSRView); + ASSERT(SUCCEEDED(result)); + if (SUCCEEDED(result)) + { + d3d11::SetDebugName(mBackBufferSRView, "Back buffer shader resource"); + } } - if (SUCCEEDED(result)) - { - d3d11::SetDebugName(mBackBufferRTView, "Back buffer render target"); - } + mFirstSwap = true; - return resetOffscreenTexture(backbufferWidth, backbufferHeight); + return resetOffscreenBuffers(backbufferWidth, backbufferHeight); } DXGI_FORMAT SwapChain11::getSwapChainNativeFormat() const @@ -417,6 +490,20 @@ DXGI_FORMAT SwapChain11::getSwapChainNativeFormat() const EGLint SwapChain11::reset(int backbufferWidth, int backbufferHeight, EGLint swapInterval) { + mSwapInterval = static_cast(swapInterval); + if (mSwapInterval > 4) + { + // IDXGISwapChain::Present documentation states that valid sync intervals are in the [0,4] + // range + return EGL_BAD_PARAMETER; + } + + // If the swap chain already exists, just resize + if (mSwapChain != nullptr) + { + return resize(backbufferWidth, backbufferHeight); + } + TRACE_EVENT0("gpu.angle", "SwapChain11::reset"); ID3D11Device *device = mRenderer->getDevice(); @@ -432,17 +519,10 @@ EGLint SwapChain11::reset(int backbufferWidth, int backbufferHeight, EGLint swap SafeRelease(mBackBufferTexture); SafeRelease(mBackBufferRTView); - mSwapInterval = static_cast(swapInterval); - if (mSwapInterval > 4) - { - // IDXGISwapChain::Present documentation states that valid sync intervals are in the [0,4] range - return EGL_BAD_PARAMETER; - } - // EGL allows creating a surface with 0x0 dimension, however, DXGI does not like 0x0 swapchains if (backbufferWidth < 1 || backbufferHeight < 1) { - releaseOffscreenTexture(); + releaseOffscreenColorBuffer(); return EGL_SUCCESS; } @@ -479,9 +559,15 @@ EGLint SwapChain11::reset(int backbufferWidth, int backbufferHeight, EGLint swap result = device->CreateRenderTargetView(mBackBufferTexture, NULL, &mBackBufferRTView); ASSERT(SUCCEEDED(result)); d3d11::SetDebugName(mBackBufferRTView, "Back buffer render target"); + + result = device->CreateShaderResourceView(mBackBufferTexture, nullptr, &mBackBufferSRView); + ASSERT(SUCCEEDED(result)); + d3d11::SetDebugName(mBackBufferSRView, "Back buffer shader resource view"); } - return resetOffscreenTexture(backbufferWidth, backbufferHeight); + mFirstSwap = true; + + return resetOffscreenBuffers(backbufferWidth, backbufferHeight); } void SwapChain11::initPassThroughResources() @@ -549,11 +635,49 @@ void SwapChain11::initPassThroughResources() ASSERT(SUCCEEDED(result)); d3d11::SetDebugName(mPassThroughPS, "Swap chain pass through pixel shader"); + // Use the default rasterizer state but without culling + D3D11_RASTERIZER_DESC rasterizerDesc; + rasterizerDesc.FillMode = D3D11_FILL_SOLID; + rasterizerDesc.CullMode = D3D11_CULL_NONE; + rasterizerDesc.FrontCounterClockwise = FALSE; + rasterizerDesc.DepthBias = 0; + rasterizerDesc.SlopeScaledDepthBias = 0.0f; + rasterizerDesc.DepthBiasClamp = 0.0f; + rasterizerDesc.DepthClipEnable = TRUE; + rasterizerDesc.ScissorEnable = FALSE; + rasterizerDesc.MultisampleEnable = FALSE; + rasterizerDesc.AntialiasedLineEnable = FALSE; + result = device->CreateRasterizerState(&rasterizerDesc, &mPassThroughRS); + ASSERT(SUCCEEDED(result)); + d3d11::SetDebugName(mPassThroughRS, "Swap chain pass through rasterizer state"); + mPassThroughResourcesInit = true; } // parameters should be validated/clamped by caller EGLint SwapChain11::swapRect(EGLint x, EGLint y, EGLint width, EGLint height) +{ + if (mNeedsOffscreenTexture) + { + EGLint result = copyOffscreenToBackbuffer(x, y, width, height); + if (result != EGL_SUCCESS) + { + return result; + } + } + + EGLint result = present(x, y, width, height); + if (result != EGL_SUCCESS) + { + return result; + } + + mRenderer->onSwap(); + + return EGL_SUCCESS; +} + +EGLint SwapChain11::copyOffscreenToBackbuffer(EGLint x, EGLint y, EGLint width, EGLint height) { if (!mSwapChain) { @@ -562,7 +686,6 @@ EGLint SwapChain11::swapRect(EGLint x, EGLint y, EGLint width, EGLint height) initPassThroughResources(); - ID3D11Device *device = mRenderer->getDevice(); ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); // Set vertices @@ -586,6 +709,16 @@ EGLint SwapChain11::swapRect(EGLint x, EGLint y, EGLint width, EGLint height) float u2 = (x + width) / float(mWidth); float v2 = (y + height) / float(mHeight); + // Invert the quad vertices depending on the surface orientation. + if ((mOrientation & EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE) != 0) + { + std::swap(x1, x2); + } + if ((mOrientation & EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE) != 0) + { + std::swap(y1, y2); + } + d3d11::SetPositionTexCoordVertex(&vertices[0], x1, y1, u1, v1); d3d11::SetPositionTexCoordVertex(&vertices[1], x1, y2, u1, v2); d3d11::SetPositionTexCoordVertex(&vertices[2], x2, y1, u2, v1); @@ -603,7 +736,7 @@ EGLint SwapChain11::swapRect(EGLint x, EGLint y, EGLint width, EGLint height) static const float blendFactor[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; deviceContext->OMSetBlendState(NULL, blendFactor, 0xFFFFFFF); - deviceContext->RSSetState(NULL); + deviceContext->RSSetState(mPassThroughRS); // Apply shaders deviceContext->IASetInputLayout(mPassThroughIL); @@ -626,7 +759,8 @@ EGLint SwapChain11::swapRect(EGLint x, EGLint y, EGLint width, EGLint height) deviceContext->RSSetViewports(1, &viewport); // Apply textures - mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, mOffscreenSRView); + auto stateManager = mRenderer->getStateManager(); + stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, mOffscreenSRView); deviceContext->PSSetSamplers(0, 1, &mPassThroughSampler); // Draw @@ -635,36 +769,60 @@ EGLint SwapChain11::swapRect(EGLint x, EGLint y, EGLint width, EGLint height) // Rendering to the swapchain is now complete. Now we can call Present(). // Before that, we perform any cleanup on the D3D device. We do this before Present() to make sure the // cleanup is caught under the current eglSwapBuffers() PIX/Graphics Diagnostics call rather than the next one. - mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, NULL); + stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, NULL); mRenderer->unapplyRenderTargets(); mRenderer->markAllStateDirty(); + return EGL_SUCCESS; +} + +EGLint SwapChain11::present(EGLint x, EGLint y, EGLint width, EGLint height) +{ + if (!mSwapChain) + { + return EGL_SUCCESS; + } + + UINT swapInterval = mSwapInterval; #if ANGLE_VSYNC == ANGLE_DISABLED - result = mSwapChain->Present(0, 0); -#else + swapInterval = 0; +#endif + + HRESULT result = S_OK; + // Use IDXGISwapChain1::Present1 with a dirty rect if DXGI 1.2 is available. if (mSwapChain1 != nullptr) { - RECT rect = + if (mFirstSwap) { - static_cast(x), static_cast(mHeight - y - height), - static_cast(x + width), static_cast(mHeight - y) - }; - DXGI_PRESENT_PARAMETERS params = { 1, &rect, nullptr, nullptr }; - result = mSwapChain1->Present1(mSwapInterval, 0, ¶ms); + // Can't swap with a dirty rect if this swap chain has never swapped before + DXGI_PRESENT_PARAMETERS params = {0, nullptr, nullptr, nullptr}; + result = mSwapChain1->Present1(swapInterval, 0, ¶ms); + } + else + { + RECT rect = {static_cast(x), static_cast(mHeight - y - height), + static_cast(x + width), static_cast(mHeight - y)}; + DXGI_PRESENT_PARAMETERS params = {1, &rect, nullptr, nullptr}; + result = mSwapChain1->Present1(swapInterval, 0, ¶ms); + } } else { - result = mSwapChain->Present(mSwapInterval, 0); + result = mSwapChain->Present(swapInterval, 0); } -#endif + + mFirstSwap = false; + + // Some swapping mechanisms such as DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL unbind the current render + // target. Mark it dirty. + mRenderer->getStateManager()->invalidateRenderTarget(); if (result == DXGI_ERROR_DEVICE_REMOVED) { - HRESULT removedReason = device->GetDeviceRemovedReason(); - UNUSED_TRACE_VARIABLE(removedReason); - ERR("Present failed: the D3D11 device was removed: 0x%08X", removedReason); + ERR("Present failed: the D3D11 device was removed: 0x%08X", + mRenderer->getDevice()->GetDeviceRemovedReason()); return EGL_CONTEXT_LOST; } else if (result == DXGI_ERROR_DEVICE_RESET) @@ -677,24 +835,24 @@ EGLint SwapChain11::swapRect(EGLint x, EGLint y, EGLint width, EGLint height) ERR("Present failed with error code 0x%08X", result); } - mRenderer->onSwap(); + mNativeWindow.commitChange(); return EGL_SUCCESS; } ID3D11Texture2D *SwapChain11::getOffscreenTexture() { - return mOffscreenTexture; + return mNeedsOffscreenTexture ? mOffscreenTexture : mBackBufferTexture; } ID3D11RenderTargetView *SwapChain11::getRenderTarget() { - return mOffscreenRTView; + return mNeedsOffscreenTexture ? mOffscreenRTView : mBackBufferRTView; } ID3D11ShaderResourceView *SwapChain11::getRenderTargetShaderResource() { - return mOffscreenSRView; + return mNeedsOffscreenTexture ? mOffscreenSRView : mBackBufferSRView; } ID3D11DepthStencilView *SwapChain11::getDepthStencil() diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h index 24c21e10e8..583e29c3c8 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h @@ -20,8 +20,12 @@ class Renderer11; class SwapChain11 : public SwapChainD3D { public: - SwapChain11(Renderer11 *renderer, NativeWindow nativeWindow, HANDLE shareHandle, - GLenum backBufferFormat, GLenum depthBufferFormat); + SwapChain11(Renderer11 *renderer, + NativeWindow nativeWindow, + HANDLE shareHandle, + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation); virtual ~SwapChain11(); EGLint resize(EGLint backbufferWidth, EGLint backbufferHeight); @@ -47,24 +51,36 @@ class SwapChain11 : public SwapChainD3D private: void release(); void initPassThroughResources(); - void releaseOffscreenTexture(); - EGLint resetOffscreenTexture(int backbufferWidth, int backbufferHeight); + + void releaseOffscreenColorBuffer(); + void releaseOffscreenDepthBuffer(); + EGLint resetOffscreenBuffers(int backbufferWidth, int backbufferHeight); + EGLint resetOffscreenColorBuffer(int backbufferWidth, int backbufferHeight); + EGLint resetOffscreenDepthBuffer(int backbufferWidth, int backbufferHeight); + DXGI_FORMAT getSwapChainNativeFormat() const; + EGLint copyOffscreenToBackbuffer(EGLint x, EGLint y, EGLint width, EGLint height); + EGLint present(EGLint x, EGLint y, EGLint width, EGLint height); + Renderer11 *mRenderer; - EGLint mHeight; EGLint mWidth; + EGLint mHeight; + const EGLint mOrientation; bool mAppCreatedShareHandle; unsigned int mSwapInterval; bool mPassThroughResourcesInit; + bool mFirstSwap; DXGISwapChain *mSwapChain; IDXGISwapChain1 *mSwapChain1; IDXGIKeyedMutex *mKeyedMutex; ID3D11Texture2D *mBackBufferTexture; ID3D11RenderTargetView *mBackBufferRTView; + ID3D11ShaderResourceView *mBackBufferSRView; + const bool mNeedsOffscreenTexture; ID3D11Texture2D *mOffscreenTexture; ID3D11RenderTargetView *mOffscreenRTView; ID3D11ShaderResourceView *mOffscreenSRView; @@ -78,6 +94,7 @@ class SwapChain11 : public SwapChainD3D ID3D11InputLayout *mPassThroughIL; ID3D11VertexShader *mPassThroughVS; ID3D11PixelShader *mPassThroughPS; + ID3D11RasterizerState *mPassThroughRS; SurfaceRenderTarget11 mColorRenderTarget; SurfaceRenderTarget11 mDepthStencilRenderTarget; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp index 3913b59ad9..11b9f76464 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp @@ -30,7 +30,10 @@ namespace rx { TextureStorage11::SwizzleCacheValue::SwizzleCacheValue() - : swizzleRed(GL_NONE), swizzleGreen(GL_NONE), swizzleBlue(GL_NONE), swizzleAlpha(GL_NONE) + : swizzleRed(GL_INVALID_INDEX), + swizzleGreen(GL_INVALID_INDEX), + swizzleBlue(GL_INVALID_INDEX), + swizzleAlpha(GL_INVALID_INDEX) { } @@ -554,8 +557,9 @@ gl::Error TextureStorage11::generateMipmap(const gl::ImageIndex &sourceIndex, co gl::Extents destSize(dest->getWidth(), dest->getHeight(), dest->getDepth()); Blit11 *blitter = mRenderer->getBlitter(); - return blitter->copyTexture(sourceSRV, sourceArea, sourceSize, destRTV, destArea, destSize, NULL, - gl::GetInternalFormatInfo(source->getInternalFormat()).format, GL_LINEAR); + return blitter->copyTexture(sourceSRV, sourceArea, sourceSize, destRTV, destArea, destSize, + NULL, gl::GetInternalFormatInfo(source->getInternalFormat()).format, + GL_LINEAR, false); } void TextureStorage11::verifySwizzleExists(GLenum swizzleRed, GLenum swizzleGreen, GLenum swizzleBlue, GLenum swizzleAlpha) @@ -663,17 +667,29 @@ gl::Error TextureStorage11::setData(const gl::ImageIndex &index, ImageD3D *image UINT bufferDepthPitch = bufferRowPitch * height; size_t neededSize = bufferDepthPitch * depth; - MemoryBuffer *conversionBuffer = NULL; - error = mRenderer->getScratchMemoryBuffer(neededSize, &conversionBuffer); - if (error.isError()) - { - return error; - } + MemoryBuffer *conversionBuffer = nullptr; + const uint8_t *data = nullptr; - // TODO: fast path - LoadImageFunction loadFunction = d3d11Format.loadFunctions.at(type); - loadFunction(width, height, depth, pixelData + srcSkipBytes, srcRowPitch, srcDepthPitch, - conversionBuffer->data(), bufferRowPitch, bufferDepthPitch); + d3d11::LoadImageFunctionInfo loadFunctionInfo = d3d11Format.loadFunctions.at(type); + if (loadFunctionInfo.requiresConversion) + { + error = mRenderer->getScratchMemoryBuffer(neededSize, &conversionBuffer); + if (error.isError()) + { + return error; + } + + loadFunctionInfo.loadFunction(width, height, depth, pixelData + srcSkipBytes, srcRowPitch, + srcDepthPitch, conversionBuffer->data(), bufferRowPitch, + bufferDepthPitch); + data = conversionBuffer->data(); + } + else + { + data = pixelData + srcSkipBytes; + bufferRowPitch = srcRowPitch; + bufferDepthPitch = srcDepthPitch; + } ID3D11DeviceContext *immediateContext = mRenderer->getDeviceContext(); @@ -689,15 +705,13 @@ gl::Error TextureStorage11::setData(const gl::ImageIndex &index, ImageD3D *image destD3DBox.front = destBox->z; destD3DBox.back = destBox->z + destBox->depth; - immediateContext->UpdateSubresource(resource, destSubresource, - &destD3DBox, conversionBuffer->data(), + immediateContext->UpdateSubresource(resource, destSubresource, &destD3DBox, data, bufferRowPitch, bufferDepthPitch); } else { - immediateContext->UpdateSubresource(resource, destSubresource, - NULL, conversionBuffer->data(), - bufferRowPitch, bufferDepthPitch); + immediateContext->UpdateSubresource(resource, destSubresource, NULL, data, bufferRowPitch, + bufferDepthPitch); } return gl::Error(GL_NO_ERROR); @@ -1097,6 +1111,8 @@ gl::Error TextureStorage11_2D::ensureTextureExists(int mipLevels) ASSERT(result == E_OUTOFMEMORY); return gl::Error(GL_OUT_OF_MEMORY, "Failed to create 2D texture storage, result: 0x%X.", result); } + + d3d11::SetDebugName(*outputTexture, "TexStorage2D.Texture"); } return gl::Error(GL_NO_ERROR); @@ -1263,6 +1279,8 @@ gl::Error TextureStorage11_2D::createSRV(int baseLevel, int mipLevels, DXGI_FORM return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal texture storage SRV, result: 0x%X.", result); } + d3d11::SetDebugName(*outSRV, "TexStorage2D.SRV"); + return gl::Error(GL_NO_ERROR); } @@ -1294,6 +1312,8 @@ gl::Error TextureStorage11_2D::getSwizzleTexture(ID3D11Resource **outTexture) { return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal swizzle texture, result: 0x%X.", result); } + + d3d11::SetDebugName(mSwizzleTexture, "TexStorage2D.SwizzleTexture"); } *outTexture = mSwizzleTexture; @@ -1533,6 +1553,8 @@ gl::Error TextureStorage11_EGLImage::getSwizzleTexture(ID3D11Resource **outTextu return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal swizzle texture, result: 0x%X.", result); } + + d3d11::SetDebugName(mSwizzleTexture, "TexStorageEGLImage.SwizzleTexture"); } *outTexture = mSwizzleTexture; @@ -1625,6 +1647,8 @@ gl::Error TextureStorage11_EGLImage::createSRV(int baseLevel, "Failed to create internal texture storage SRV, result: 0x%X.", result); } + + d3d11::SetDebugName(*outSRV, "TexStorageEGLImage.SRV"); } else { @@ -2068,6 +2092,8 @@ gl::Error TextureStorage11_Cube::ensureTextureExists(int mipLevels) ASSERT(result == E_OUTOFMEMORY); return gl::Error(GL_OUT_OF_MEMORY, "Failed to create cube texture storage, result: 0x%X.", result); } + + d3d11::SetDebugName(*outputTexture, "TexStorageCube.Texture"); } return gl::Error(GL_NO_ERROR); @@ -2149,6 +2175,8 @@ gl::Error TextureStorage11_Cube::getRenderTarget(const gl::ImageIndex &index, Re return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal shader resource view for texture storage, result: 0x%X.", result); } + d3d11::SetDebugName(srv, "TexStorageCube.RenderTargetSRV"); + if (mRenderTargetFormat != DXGI_FORMAT_UNKNOWN) { D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; @@ -2168,6 +2196,8 @@ gl::Error TextureStorage11_Cube::getRenderTarget(const gl::ImageIndex &index, Re return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal render target view for texture storage, result: 0x%X.", result); } + d3d11::SetDebugName(rtv, "TexStorageCube.RenderTargetRTV"); + mRenderTarget[faceIndex][level] = new TextureRenderTarget11(rtv, texture, srv, mInternalFormat, getLevelWidth(level), getLevelHeight(level), 1, 0); // RenderTarget will take ownership of these resources @@ -2194,6 +2224,8 @@ gl::Error TextureStorage11_Cube::getRenderTarget(const gl::ImageIndex &index, Re return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal depth stencil view for texture storage, result: 0x%X.", result); } + d3d11::SetDebugName(dsv, "TexStorageCube.RenderTargetDSV"); + mRenderTarget[faceIndex][level] = new TextureRenderTarget11(dsv, texture, srv, mInternalFormat, getLevelWidth(level), getLevelHeight(level), 1, 0); // RenderTarget will take ownership of these resources @@ -2225,7 +2257,7 @@ gl::Error TextureStorage11_Cube::createSRV(int baseLevel, int mipLevels, DXGI_FO { srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; srvDesc.Texture2DArray.MostDetailedMip = mTopLevel + baseLevel; - srvDesc.Texture2DArray.MipLevels = 1; + srvDesc.Texture2DArray.MipLevels = mipLevels; srvDesc.Texture2DArray.FirstArraySlice = 0; srvDesc.Texture2DArray.ArraySize = CUBE_FACE_COUNT; } @@ -2267,6 +2299,8 @@ gl::Error TextureStorage11_Cube::createSRV(int baseLevel, int mipLevels, DXGI_FO return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal texture storage SRV, result: 0x%X.", result); } + d3d11::SetDebugName(*outSRV, "TexStorageCube.SRV"); + return gl::Error(GL_NO_ERROR); } @@ -2298,6 +2332,8 @@ gl::Error TextureStorage11_Cube::getSwizzleTexture(ID3D11Resource **outTexture) { return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal swizzle texture, result: 0x%X.", result); } + + d3d11::SetDebugName(*outTexture, "TexStorageCube.SwizzleTexture"); } *outTexture = mSwizzleTexture; @@ -2520,6 +2556,8 @@ gl::Error TextureStorage11_3D::getResource(ID3D11Resource **outResource) ASSERT(result == E_OUTOFMEMORY); return gl::Error(GL_OUT_OF_MEMORY, "Failed to create 3D texture storage, result: 0x%X.", result); } + + d3d11::SetDebugName(mTexture, "TexStorage3D.Texture"); } *outResource = mTexture; @@ -2546,6 +2584,8 @@ gl::Error TextureStorage11_3D::createSRV(int baseLevel, int mipLevels, DXGI_FORM return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal texture storage SRV, result: 0x%X.", result); } + d3d11::SetDebugName(*outSRV, "TexStorage3D.SRV"); + return gl::Error(GL_NO_ERROR); } @@ -2593,6 +2633,8 @@ gl::Error TextureStorage11_3D::getRenderTarget(const gl::ImageIndex &index, Rend return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal render target view for texture storage, result: 0x%X.", result); } + d3d11::SetDebugName(rtv, "TexStorage3D.RTV"); + mLevelRenderTargets[mipLevel] = new TextureRenderTarget11(rtv, texture, srv, mInternalFormat, getLevelWidth(mipLevel), getLevelHeight(mipLevel), getLevelDepth(mipLevel), 0); // RenderTarget will take ownership of these resources @@ -2640,6 +2682,8 @@ gl::Error TextureStorage11_3D::getRenderTarget(const gl::ImageIndex &index, Rend } ASSERT(SUCCEEDED(result)); + d3d11::SetDebugName(rtv, "TexStorage3D.LayerRTV"); + mLevelLayerRenderTargets[key] = new TextureRenderTarget11(rtv, texture, srv, mInternalFormat, getLevelWidth(mipLevel), getLevelHeight(mipLevel), 1, 0); // RenderTarget will take ownership of these resources @@ -2678,6 +2722,8 @@ gl::Error TextureStorage11_3D::getSwizzleTexture(ID3D11Resource **outTexture) { return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal swizzle texture, result: 0x%X.", result); } + + d3d11::SetDebugName(mSwizzleTexture, "TexStorage3D.SwizzleTexture"); } *outTexture = mSwizzleTexture; @@ -2714,6 +2760,8 @@ gl::Error TextureStorage11_3D::getSwizzleRenderTarget(int mipLevel, ID3D11Render { return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal swizzle render target view, result: 0x%X.", result); } + + d3d11::SetDebugName(mSwizzleTexture, "TexStorage3D.SwizzleRTV"); } *outRTV = mSwizzleRenderTargets[mipLevel]; @@ -2899,6 +2947,8 @@ gl::Error TextureStorage11_2DArray::getResource(ID3D11Resource **outResource) ASSERT(result == E_OUTOFMEMORY); return gl::Error(GL_OUT_OF_MEMORY, "Failed to create 2D array texture storage, result: 0x%X.", result); } + + d3d11::SetDebugName(mTexture, "TexStorage2DArray.Texture"); } *outResource = mTexture; @@ -2925,6 +2975,8 @@ gl::Error TextureStorage11_2DArray::createSRV(int baseLevel, int mipLevels, DXGI return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal texture storage SRV, result: 0x%X.", result); } + d3d11::SetDebugName(*outSRV, "TexStorage2DArray.SRV"); + return gl::Error(GL_NO_ERROR); } @@ -2967,6 +3019,8 @@ gl::Error TextureStorage11_2DArray::getRenderTarget(const gl::ImageIndex &index, return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal shader resource view for texture storage, result: 0x%X.", result); } + d3d11::SetDebugName(srv, "TexStorage2DArray.RenderTargetSRV"); + if (mRenderTargetFormat != DXGI_FORMAT_UNKNOWN) { D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; @@ -2986,6 +3040,8 @@ gl::Error TextureStorage11_2DArray::getRenderTarget(const gl::ImageIndex &index, return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal render target view for texture storage, result: 0x%X.", result); } + d3d11::SetDebugName(rtv, "TexStorage2DArray.RenderTargetRTV"); + mRenderTargets[key] = new TextureRenderTarget11(rtv, texture, srv, mInternalFormat, getLevelWidth(mipLevel), getLevelHeight(mipLevel), 1, 0); // RenderTarget will take ownership of these resources @@ -3029,6 +3085,8 @@ gl::Error TextureStorage11_2DArray::getSwizzleTexture(ID3D11Resource **outTextur { return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal swizzle texture, result: 0x%X.", result); } + + d3d11::SetDebugName(*outTexture, "TexStorage2DArray.SwizzleTexture"); } *outTexture = mSwizzleTexture; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_load_functions_table.py b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_load_functions_table.py index 0985ba8c68..8807f9ff41 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_load_functions_table.py +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_load_functions_table.py @@ -22,6 +22,7 @@ template = """// GENERATED FILE - DO NOT EDIT. #include "libANGLE/renderer/d3d/d3d11/load_functions_table.h" #include "libANGLE/renderer/d3d/d3d11/formatutils11.h" +#include "libANGLE/renderer/d3d/d3d11/texture_format_table.h" #include "libANGLE/renderer/d3d/loadimage.h" #include "libANGLE/renderer/d3d/loadimage_etc.h" @@ -70,8 +71,8 @@ void UnreachableLoadFunction(size_t width, }} // namespace // TODO we can replace these maps with more generated code -const std::map &GetLoadFunctionsMap(GLenum {internal_format}, - DXGI_FORMAT {dxgi_format}) +const std::map &GetLoadFunctionsMap(GLenum {internal_format}, + DXGI_FORMAT {dxgi_format}) {{ // clang-format off switch ({internal_format}) @@ -79,7 +80,7 @@ const std::map &GetLoadFunctionsMap(GLenum {internal_ {data} default: {{ - static std::map emptyLoadFunctionsMap; + static std::map emptyLoadFunctionsMap; return emptyLoadFunctionsMap; }} }} @@ -96,8 +97,8 @@ internal_format_param = 'internalFormat' dxgi_format_param = 'dxgiFormat' dxgi_format_unknown = "DXGI_FORMAT_UNKNOWN" -def get_function_maps_string(typestr, function): - return ' loadMap[' + typestr + '] = ' + function + ';\n' +def get_function_maps_string(typestr, function, requiresConversion): + return ' loadMap[' + typestr + '] = LoadImageFunctionInfo(' + function + ', ' + requiresConversion + ');\n' def get_unknown_format_string(dxgi_to_type_map, dxgi_unknown_string): if dxgi_unknown_string not in dxgi_to_type_map: @@ -106,7 +107,7 @@ def get_unknown_format_string(dxgi_to_type_map, dxgi_unknown_string): table_data = '' for unknown_type_function in dxgi_to_type_map[dxgi_unknown_string]: - table_data += get_function_maps_string(unknown_type_function['type'], unknown_type_function['loadFunction']) + table_data += get_function_maps_string(unknown_type_function['type'], unknown_type_function['loadFunction'], 'true') return table_data @@ -125,8 +126,8 @@ def create_dxgi_to_type_map(dst, json_data, internal_format_str): def get_load_function_map_snippet(insert_map_string): load_function_map_snippet = '' - load_function_map_snippet += ' static const std::map loadFunctionsMap = []() {\n' - load_function_map_snippet += ' std::map loadMap;\n' + load_function_map_snippet += ' static const std::map loadFunctionsMap = []() {\n' + load_function_map_snippet += ' std::map loadMap;\n' load_function_map_snippet += insert_map_string load_function_map_snippet += ' return loadMap;\n' load_function_map_snippet += ' }();\n\n' @@ -157,8 +158,7 @@ def parse_json_into_switch_string(json_data): insert_map_string = '' types_already_in_loadmap = set() for type_function in sorted(dxgi_format_item[1]): - # type_function['requiresConversion'] element is not in use at the moment but may be needed later - insert_map_string += get_function_maps_string(type_function['type'], type_function['loadFunction']) + insert_map_string += get_function_maps_string(type_function['type'], type_function['loadFunction'], type_function['requiresConversion']) types_already_in_loadmap.add(type_function['type']) # DXGI_FORMAT_UNKNOWN add ons @@ -166,7 +166,7 @@ def parse_json_into_switch_string(json_data): for unknown_type_function in dxgi_to_type_map[dxgi_format_unknown]: # Check that it's not already in the loadmap so it doesn't override the value if unknown_type_function['type'] not in types_already_in_loadmap: - insert_map_string += get_function_maps_string(unknown_type_function['type'], unknown_type_function['loadFunction']) + insert_map_string += get_function_maps_string(unknown_type_function['type'], unknown_type_function['loadFunction'], 'true') table_data += get_load_function_map_snippet(insert_map_string) table_data += ' }\n' diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_texture_format_table.py b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_texture_format_table.py index b065466df6..d0ad86b62e 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_texture_format_table.py +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_texture_format_table.py @@ -248,24 +248,25 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, }} // namespace rx """ -tex_format = "texFormat" -srv_format = "srvFormat" -rtv_format = "rtvFormat" -dsv_format = "dsvFormat" - def get_texture_format_item(idx, texture_format): table_data = ''; + tex_format = texture_format["texFormat"] if "texFormat" in texture_format else "DXGI_FORMAT_UNKNOWN" + srv_format = texture_format["srvFormat"] if "srvFormat" in texture_format else "DXGI_FORMAT_UNKNOWN" + rtv_format = texture_format["rtvFormat"] if "rtvFormat" in texture_format else "DXGI_FORMAT_UNKNOWN" + dsv_format = texture_format["dsvFormat"] if "dsvFormat" in texture_format else "DXGI_FORMAT_UNKNOWN" + requirements_fn = texture_format["requirementsFcn"] if "requirementsFcn" in texture_format else "AnyDevice" + if idx == 0: - table_data += ' if (' + texture_format["requirementsFcn"] + '(renderer11DeviceCaps))\n' + table_data += ' if (' + requirements_fn + '(renderer11DeviceCaps))\n' else: - table_data += ' else if (' + texture_format["requirementsFcn"] + '(renderer11DeviceCaps))\n' + table_data += ' else if (' + requirements_fn + '(renderer11DeviceCaps))\n' table_data += ' {\n' table_data += ' static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat,\n' - table_data += ' ' + texture_format[tex_format] + ',\n' - table_data += ' ' + texture_format[srv_format] + ',\n' - table_data += ' ' + texture_format[rtv_format] + ',\n' - table_data += ' ' + texture_format[dsv_format] + ');\n' + table_data += ' ' + tex_format + ',\n' + table_data += ' ' + srv_format + ',\n' + table_data += ' ' + rtv_format + ',\n' + table_data += ' ' + dsv_format + ');\n' table_data += ' return textureFormat;\n' table_data += ' }\n' diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_data.json b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_data.json index cb1d8595a2..c85393e06b 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_data.json +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_data.json @@ -1103,5 +1103,14 @@ "requiresConversion": "true" } ] + }, + "GL_ETC1_RGB8_LOSSY_DECODE_ANGLE": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadETC1RGB8ToBC1", + "dxgiFormat": "DXGI_FORMAT_BC1_UNORM", + "requiresConversion": "true" + } + ] } } \ No newline at end of file diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table.h index 60ab41fd83..b17062f68d 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table.h @@ -13,6 +13,7 @@ #include #include "libANGLE/renderer/d3d/d3d11/Renderer11.h" +#include "libANGLE/renderer/d3d/d3d11/texture_format_table.h" namespace rx { @@ -20,8 +21,8 @@ namespace rx namespace d3d11 { -const std::map &GetLoadFunctionsMap(GLenum internalFormat, - DXGI_FORMAT dxgiFormat); +const std::map &GetLoadFunctionsMap(GLenum internalFormat, + DXGI_FORMAT dxgiFormat); } // namespace d3d11 diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table_autogen.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table_autogen.cpp index 788f792e7b..acb48b9573 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table_autogen.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table_autogen.cpp @@ -11,6 +11,7 @@ #include "libANGLE/renderer/d3d/d3d11/load_functions_table.h" #include "libANGLE/renderer/d3d/d3d11/formatutils11.h" +#include "libANGLE/renderer/d3d/d3d11/texture_format_table.h" #include "libANGLE/renderer/d3d/loadimage.h" #include "libANGLE/renderer/d3d/loadimage_etc.h" @@ -59,8 +60,8 @@ void UnreachableLoadFunction(size_t width, } // namespace // TODO we can replace these maps with more generated code -const std::map &GetLoadFunctionsMap(GLenum internalFormat, - DXGI_FORMAT dxgiFormat) +const std::map &GetLoadFunctionsMap(GLenum internalFormat, + DXGI_FORMAT dxgiFormat) { // clang-format off switch (internalFormat) @@ -71,11 +72,11 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16G16B16A16_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_HALF_FLOAT] = LoadA16FToRGBA16F; - loadMap[GL_HALF_FLOAT_OES] = LoadA16FToRGBA16F; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadA16FToRGBA16F, true); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadA16FToRGBA16F, true); + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -83,10 +84,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_R32G32B32A32_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadA32FToRGBA32F; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadA32FToRGBA32F, true); + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -94,9 +95,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -104,9 +105,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -120,10 +121,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_HALF_FLOAT] = LoadA16FToRGBA16F; - loadMap[GL_HALF_FLOAT_OES] = LoadA16FToRGBA16F; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadA16FToRGBA16F, true); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadA16FToRGBA16F, true); return loadMap; }(); @@ -131,10 +132,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_HALF_FLOAT] = LoadA16FToRGBA16F; - loadMap[GL_HALF_FLOAT_OES] = LoadA16FToRGBA16F; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadA16FToRGBA16F, true); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadA16FToRGBA16F, true); return loadMap; }(); @@ -148,9 +149,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadA32FToRGBA32F; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadA32FToRGBA32F, true); return loadMap; }(); @@ -158,9 +159,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadA32FToRGBA32F; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadA32FToRGBA32F, true); return loadMap; }(); @@ -174,9 +175,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_A8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -184,9 +185,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_R8G8B8A8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadA8ToRGBA8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadA8ToRGBA8, true); return loadMap; }(); @@ -202,10 +203,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT] = LoadRGB5A1ToRGBA8; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT] = LoadImageFunctionInfo(LoadRGB5A1ToRGBA8, true); + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -213,10 +214,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; - loadMap[GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT] = LoadRGB5A1ToRGBA8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, true); + loadMap[GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT] = LoadImageFunctionInfo(LoadRGB5A1ToRGBA8, true); return loadMap; }(); @@ -230,10 +231,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT] = LoadRGBA4ToRGBA8; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT] = LoadImageFunctionInfo(LoadRGBA4ToRGBA8, true); + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -241,10 +242,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; - loadMap[GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT] = LoadRGBA4ToRGBA8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, true); + loadMap[GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT] = LoadImageFunctionInfo(LoadRGBA4ToRGBA8, true); return loadMap; }(); @@ -258,9 +259,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -268,9 +269,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, true); return loadMap; }(); @@ -284,9 +285,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -294,9 +295,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -310,9 +311,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadEACR11ToR8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadEACR11ToR8, true); return loadMap; }(); @@ -328,9 +329,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadEACRG11ToRG8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadEACRG11ToRG8, true); return loadMap; }(); @@ -346,9 +347,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadETC2RGB8ToRGBA8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadETC2RGB8ToRGBA8, true); return loadMap; }(); @@ -364,9 +365,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadETC2RGB8A1ToRGBA8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadETC2RGB8A1ToRGBA8, true); return loadMap; }(); @@ -382,9 +383,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadETC2RGBA8ToRGBA8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadETC2RGBA8ToRGBA8, true); return loadMap; }(); @@ -400,9 +401,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,8>; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadCompressedToNative<4,4,8>, true); return loadMap; }(); @@ -410,9 +411,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,8>; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadCompressedToNative<4,4,8>, true); return loadMap; }(); @@ -426,9 +427,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,16>; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadCompressedToNative<4,4,16>, true); return loadMap; }(); @@ -436,9 +437,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,16>; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadCompressedToNative<4,4,16>, true); return loadMap; }(); @@ -452,9 +453,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,16>; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadCompressedToNative<4,4,16>, true); return loadMap; }(); @@ -462,9 +463,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,16>; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadCompressedToNative<4,4,16>, true); return loadMap; }(); @@ -478,9 +479,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,8>; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadCompressedToNative<4,4,8>, true); return loadMap; }(); @@ -488,9 +489,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,8>; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadCompressedToNative<4,4,8>, true); return loadMap; }(); @@ -504,9 +505,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8_SNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadEACR11SToR8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadEACR11SToR8, true); return loadMap; }(); @@ -522,9 +523,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8_SNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadEACRG11SToRG8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadEACRG11SToRG8, true); return loadMap; }(); @@ -540,9 +541,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadETC2SRGBA8ToSRGBA8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadETC2SRGBA8ToSRGBA8, true); return loadMap; }(); @@ -558,9 +559,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadETC2SRGB8ToRGBA8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadETC2SRGB8ToRGBA8, true); return loadMap; }(); @@ -576,9 +577,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadETC2SRGB8A1ToRGBA8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadETC2SRGB8A1ToRGBA8, true); return loadMap; }(); @@ -594,9 +595,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_D24_UNORM_S8_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_INT_24_8] = LoadR32ToR24G8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT_24_8] = LoadImageFunctionInfo(LoadR32ToR24G8, true); return loadMap; }(); @@ -604,9 +605,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_R24G8_TYPELESS: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_INT_24_8] = LoadR32ToR24G8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT_24_8] = LoadImageFunctionInfo(LoadR32ToR24G8, true); return loadMap; }(); @@ -622,9 +623,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R32G8X24_TYPELESS: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT_32_UNSIGNED_INT_24_8_REV] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT_32_UNSIGNED_INT_24_8_REV] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -632,9 +633,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT_32_UNSIGNED_INT_24_8_REV] = UnimplementedLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT_32_UNSIGNED_INT_24_8_REV] = LoadImageFunctionInfo(UnimplementedLoadFunction, true); return loadMap; }(); @@ -642,9 +643,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT_32_UNSIGNED_INT_24_8_REV] = UnimplementedLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT_32_UNSIGNED_INT_24_8_REV] = LoadImageFunctionInfo(UnimplementedLoadFunction, true); return loadMap; }(); @@ -658,9 +659,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_D16_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_SHORT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -668,10 +669,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_R16_TYPELESS: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_INT] = LoadR32ToR16; - loadMap[GL_UNSIGNED_SHORT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadImageFunctionInfo(LoadR32ToR16, true); + loadMap[GL_UNSIGNED_SHORT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -687,9 +688,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_D24_UNORM_S8_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_INT] = LoadR32ToR24G8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadImageFunctionInfo(LoadR32ToR24G8, true); return loadMap; }(); @@ -697,9 +698,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_R24G8_TYPELESS: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_INT] = LoadR32ToR24G8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadImageFunctionInfo(LoadR32ToR24G8, true); return loadMap; }(); @@ -715,9 +716,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R32_TYPELESS: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -725,9 +726,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = UnimplementedLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(UnimplementedLoadFunction, true); return loadMap; }(); @@ -735,9 +736,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = UnimplementedLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(UnimplementedLoadFunction, true); return loadMap; }(); @@ -751,9 +752,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_INT] = LoadR32ToR24G8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadImageFunctionInfo(LoadR32ToR24G8, true); return loadMap; }(); @@ -761,9 +762,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_INT] = LoadR32ToR24G8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadImageFunctionInfo(LoadR32ToR24G8, true); return loadMap; }(); @@ -771,15 +772,33 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } } } + case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_BC1_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadETC1RGB8ToBC1, true); + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } case GL_ETC1_RGB8_OES: { switch (dxgiFormat) { case DXGI_FORMAT_R8G8B8A8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadETC1RGB8ToRGBA8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadETC1RGB8ToRGBA8, true); return loadMap; }(); @@ -795,11 +814,11 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16G16B16A16_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_HALF_FLOAT] = LoadL16FToRGBA16F; - loadMap[GL_HALF_FLOAT_OES] = LoadL16FToRGBA16F; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadL16FToRGBA16F, true); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadL16FToRGBA16F, true); + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -807,10 +826,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_R32G32B32A32_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadL32FToRGBA32F; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadL32FToRGBA32F, true); + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -818,9 +837,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -828,9 +847,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -844,10 +863,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_HALF_FLOAT] = LoadL16FToRGBA16F; - loadMap[GL_HALF_FLOAT_OES] = LoadL16FToRGBA16F; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadL16FToRGBA16F, true); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadL16FToRGBA16F, true); return loadMap; }(); @@ -855,10 +874,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_HALF_FLOAT] = LoadL16FToRGBA16F; - loadMap[GL_HALF_FLOAT_OES] = LoadL16FToRGBA16F; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadL16FToRGBA16F, true); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadL16FToRGBA16F, true); return loadMap; }(); @@ -872,9 +891,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadL32FToRGBA32F; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadL32FToRGBA32F, true); return loadMap; }(); @@ -882,9 +901,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadL32FToRGBA32F; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadL32FToRGBA32F, true); return loadMap; }(); @@ -898,9 +917,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadLA8ToRGBA8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadLA8ToRGBA8, true); return loadMap; }(); @@ -908,9 +927,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadLA8ToRGBA8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadLA8ToRGBA8, true); return loadMap; }(); @@ -924,9 +943,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadL8ToRGBA8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadL8ToRGBA8, true); return loadMap; }(); @@ -934,9 +953,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadL8ToRGBA8; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadL8ToRGBA8, true); return loadMap; }(); @@ -950,11 +969,11 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16G16B16A16_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_HALF_FLOAT] = LoadLA16FToRGBA16F; - loadMap[GL_HALF_FLOAT_OES] = LoadLA16FToRGBA16F; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadLA16FToRGBA16F, true); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadLA16FToRGBA16F, true); + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -962,10 +981,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_R32G32B32A32_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadLA32FToRGBA32F; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadLA32FToRGBA32F, true); + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -973,9 +992,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -983,9 +1002,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -999,10 +1018,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_HALF_FLOAT] = LoadLA16FToRGBA16F; - loadMap[GL_HALF_FLOAT_OES] = LoadLA16FToRGBA16F; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadLA16FToRGBA16F, true); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadLA16FToRGBA16F, true); return loadMap; }(); @@ -1010,10 +1029,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_HALF_FLOAT] = LoadLA16FToRGBA16F; - loadMap[GL_HALF_FLOAT_OES] = LoadLA16FToRGBA16F; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadLA16FToRGBA16F, true); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadLA16FToRGBA16F, true); return loadMap; }(); @@ -1027,9 +1046,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadLA32FToRGBA32F; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadLA32FToRGBA32F, true); return loadMap; }(); @@ -1037,9 +1056,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadLA32FToRGBA32F; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadLA32FToRGBA32F, true); return loadMap; }(); @@ -1053,12 +1072,12 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R11G11B10_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_HALF_FLOAT] = LoadRGB16FToRG11B10F; - loadMap[GL_HALF_FLOAT_OES] = LoadRGB16FToRG11B10F; - loadMap[GL_FLOAT] = LoadRGB32FToRG11B10F; - loadMap[GL_UNSIGNED_INT_10F_11F_11F_REV] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadRGB16FToRG11B10F, true); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadRGB16FToRG11B10F, true); + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadRGB32FToRG11B10F, true); + loadMap[GL_UNSIGNED_INT_10F_11F_11F_REV] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1074,11 +1093,11 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = Load32FTo16F<1>; - loadMap[GL_HALF_FLOAT] = LoadToNative; - loadMap[GL_HALF_FLOAT_OES] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(Load32FTo16F<1>, true); + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadToNative, false); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1094,9 +1113,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16_SINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_SHORT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_SHORT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1112,9 +1131,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_SHORT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1130,9 +1149,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R32_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1148,9 +1167,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R32_SINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_INT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_INT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1166,9 +1185,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R32_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_INT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1184,9 +1203,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1202,9 +1221,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8_SINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1220,9 +1239,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1238,9 +1257,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8_SNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1256,11 +1275,11 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16G16_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = Load32FTo16F<2>; - loadMap[GL_HALF_FLOAT] = LoadToNative; - loadMap[GL_HALF_FLOAT_OES] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(Load32FTo16F<2>, true); + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadToNative, false); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1276,9 +1295,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16G16_SINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_SHORT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_SHORT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1294,9 +1313,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16G16_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_SHORT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1312,9 +1331,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R32G32_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1330,9 +1349,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R32G32_SINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_INT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_INT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1348,9 +1367,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R32G32_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_INT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1366,9 +1385,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1384,9 +1403,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8_SINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1402,9 +1421,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1420,9 +1439,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8_SNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1438,10 +1457,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; - loadMap[GL_UNSIGNED_SHORT_5_6_5] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); + loadMap[GL_UNSIGNED_SHORT_5_6_5] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -1449,10 +1468,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; - loadMap[GL_UNSIGNED_SHORT_5_6_5] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); + loadMap[GL_UNSIGNED_SHORT_5_6_5] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -1466,9 +1485,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R10G10B10A2_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_INT_2_10_10_10_REV] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT_2_10_10_10_REV] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1484,9 +1503,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R10G10B10A2_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_INT_2_10_10_10_REV] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT_2_10_10_10_REV] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1502,11 +1521,11 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16G16B16A16_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadRGB32FToRGBA16F; - loadMap[GL_HALF_FLOAT] = LoadToNative3To4; - loadMap[GL_HALF_FLOAT_OES] = LoadToNative3To4; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadRGB32FToRGBA16F, true); + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadToNative3To4, true); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadToNative3To4, true); return loadMap; }(); @@ -1522,9 +1541,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16G16B16A16_SINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_SHORT] = LoadToNative3To4; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_SHORT] = LoadImageFunctionInfo(LoadToNative3To4, true); return loadMap; }(); @@ -1540,9 +1559,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16G16B16A16_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_SHORT] = LoadToNative3To4; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT] = LoadImageFunctionInfo(LoadToNative3To4, true); return loadMap; }(); @@ -1558,9 +1577,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R32G32B32A32_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadToNative3To4; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadToNative3To4, true); return loadMap; }(); @@ -1576,9 +1595,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R32G32B32A32_SINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_INT] = LoadToNative3To4; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_INT] = LoadImageFunctionInfo(LoadToNative3To4, true); return loadMap; }(); @@ -1594,9 +1613,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R32G32B32A32_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_INT] = LoadToNative3To4; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadImageFunctionInfo(LoadToNative3To4, true); return loadMap; }(); @@ -1612,9 +1631,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_B5G6R5_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_SHORT_5_6_5] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT_5_6_5] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1622,10 +1641,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_R8G8B8A8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_SHORT_5_6_5] = LoadR5G6B5ToRGBA8; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative3To4; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT_5_6_5] = LoadImageFunctionInfo(LoadR5G6B5ToRGBA8, true); + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative3To4, true); return loadMap; }(); @@ -1641,9 +1660,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_B5G5R5A1_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_SHORT_5_5_5_1] = LoadRGB5A1ToA1RGB5; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT_5_5_5_1] = LoadImageFunctionInfo(LoadRGB5A1ToA1RGB5, true); return loadMap; }(); @@ -1651,11 +1670,11 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_R8G8B8A8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_INT_2_10_10_10_REV] = LoadRGB10A2ToRGBA8; - loadMap[GL_UNSIGNED_SHORT_5_5_5_1] = LoadRGB5A1ToRGBA8; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT_2_10_10_10_REV] = LoadImageFunctionInfo(LoadRGB10A2ToRGBA8, true); + loadMap[GL_UNSIGNED_SHORT_5_5_5_1] = LoadImageFunctionInfo(LoadRGB5A1ToRGBA8, true); + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1671,9 +1690,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative3To4; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative3To4, true); return loadMap; }(); @@ -1689,9 +1708,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_SINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_BYTE] = LoadToNative3To4; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadImageFunctionInfo(LoadToNative3To4, true); return loadMap; }(); @@ -1707,9 +1726,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative3To4; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative3To4, true); return loadMap; }(); @@ -1725,9 +1744,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_SNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_BYTE] = LoadToNative3To4; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadImageFunctionInfo(LoadToNative3To4, true); return loadMap; }(); @@ -1743,12 +1762,12 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_HALF_FLOAT] = LoadRGB16FToRGB9E5; - loadMap[GL_HALF_FLOAT_OES] = LoadRGB16FToRGB9E5; - loadMap[GL_FLOAT] = LoadRGB32FToRGB9E5; - loadMap[GL_UNSIGNED_INT_5_9_9_9_REV] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadRGB16FToRGB9E5, true); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadRGB16FToRGB9E5, true); + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadRGB32FToRGB9E5, true); + loadMap[GL_UNSIGNED_INT_5_9_9_9_REV] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1764,11 +1783,11 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; - loadMap[GL_UNSIGNED_SHORT_4_4_4_4] = UnreachableLoadFunction; - loadMap[GL_UNSIGNED_SHORT_5_5_5_1] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); + loadMap[GL_UNSIGNED_SHORT_4_4_4_4] = LoadImageFunctionInfo(UnreachableLoadFunction, true); + loadMap[GL_UNSIGNED_SHORT_5_5_5_1] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -1776,11 +1795,11 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; - loadMap[GL_UNSIGNED_SHORT_4_4_4_4] = UnreachableLoadFunction; - loadMap[GL_UNSIGNED_SHORT_5_5_5_1] = UnreachableLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(UnreachableLoadFunction, true); + loadMap[GL_UNSIGNED_SHORT_4_4_4_4] = LoadImageFunctionInfo(UnreachableLoadFunction, true); + loadMap[GL_UNSIGNED_SHORT_5_5_5_1] = LoadImageFunctionInfo(UnreachableLoadFunction, true); return loadMap; }(); @@ -1794,11 +1813,11 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16G16B16A16_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = Load32FTo16F<4>; - loadMap[GL_HALF_FLOAT] = LoadToNative; - loadMap[GL_HALF_FLOAT_OES] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(Load32FTo16F<4>, true); + loadMap[GL_HALF_FLOAT] = LoadImageFunctionInfo(LoadToNative, false); + loadMap[GL_HALF_FLOAT_OES] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1814,9 +1833,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16G16B16A16_SINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_SHORT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_SHORT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1832,9 +1851,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R16G16B16A16_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_SHORT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1850,9 +1869,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R32G32B32A32_FLOAT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_FLOAT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1868,9 +1887,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R32G32B32A32_SINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_INT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_INT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1886,9 +1905,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R32G32B32A32_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_INT] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1904,9 +1923,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_B4G4R4A4_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_SHORT_4_4_4_4] = LoadRGBA4ToARGB4; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT_4_4_4_4] = LoadImageFunctionInfo(LoadRGBA4ToARGB4, true); return loadMap; }(); @@ -1914,10 +1933,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } case DXGI_FORMAT_R8G8B8A8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_SHORT_4_4_4_4] = LoadRGBA4ToRGBA8; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT_4_4_4_4] = LoadImageFunctionInfo(LoadRGBA4ToRGBA8, true); + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1933,9 +1952,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_UNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1951,9 +1970,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_SINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1969,9 +1988,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_UINT: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -1987,9 +2006,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_SNORM: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -2005,9 +2024,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative3To4; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative3To4, true); return loadMap; }(); @@ -2023,9 +2042,9 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadImageFunctionInfo(LoadToNative, false); return loadMap; }(); @@ -2041,10 +2060,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo { case DXGI_FORMAT_UNKNOWN: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[DXGI_FORMAT_D24_UNORM_S8_UINT] = UnimplementedLoadFunction; - loadMap[DXGI_FORMAT_R24G8_TYPELESS] = UnimplementedLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[DXGI_FORMAT_D24_UNORM_S8_UINT] = LoadImageFunctionInfo(UnimplementedLoadFunction, true); + loadMap[DXGI_FORMAT_R24G8_TYPELESS] = LoadImageFunctionInfo(UnimplementedLoadFunction, true); return loadMap; }(); @@ -2052,10 +2071,10 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo } default: { - static const std::map loadFunctionsMap = []() { - std::map loadMap; - loadMap[DXGI_FORMAT_D24_UNORM_S8_UINT] = UnimplementedLoadFunction; - loadMap[DXGI_FORMAT_R24G8_TYPELESS] = UnimplementedLoadFunction; + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[DXGI_FORMAT_D24_UNORM_S8_UINT] = LoadImageFunctionInfo(UnimplementedLoadFunction, true); + loadMap[DXGI_FORMAT_R24G8_TYPELESS] = LoadImageFunctionInfo(UnimplementedLoadFunction, true); return loadMap; }(); @@ -2066,7 +2085,7 @@ const std::map &GetLoadFunctionsMap(GLenum internalFo default: { - static std::map emptyLoadFunctionsMap; + static std::map emptyLoadFunctionsMap; return emptyLoadFunctionsMap; } } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp index e8058cc631..d1f3ea0e25 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp @@ -221,12 +221,14 @@ D3D11_QUERY ConvertQueryType(GLenum queryType) case GL_ANY_SAMPLES_PASSED_EXT: case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT: return D3D11_QUERY_OCCLUSION; case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: return D3D11_QUERY_SO_STATISTICS; + case GL_TIME_ELAPSED_EXT: + // Two internal queries are also created for begin/end timestamps + return D3D11_QUERY_TIMESTAMP_DISJOINT; default: UNREACHABLE(); return D3D11_QUERY_EVENT; } } -} - +} // namespace gl_d3d11 namespace d3d11_gl { @@ -290,7 +292,7 @@ unsigned int GetReservedVertexUniformVectors(D3D_FEATURE_LEVEL featureLevel) case D3D_FEATURE_LEVEL_9_3: case D3D_FEATURE_LEVEL_9_2: case D3D_FEATURE_LEVEL_9_1: - return 2; // dx_ViewAdjust and dx_ViewCoords + return 3; // dx_ViewAdjust, dx_ViewCoords and dx_ViewScale default: UNREACHABLE(); @@ -311,7 +313,7 @@ unsigned int GetReservedFragmentUniformVectors(D3D_FEATURE_LEVEL featureLevel) case D3D_FEATURE_LEVEL_9_3: case D3D_FEATURE_LEVEL_9_2: case D3D_FEATURE_LEVEL_9_1: - return 2; + return 3; default: UNREACHABLE(); @@ -1199,6 +1201,10 @@ void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, cons extensions->occlusionQueryBoolean = GetOcclusionQuerySupport(featureLevel); extensions->fence = GetEventQuerySupport(featureLevel); extensions->timerQuery = false; // Unimplemented + extensions->disjointTimerQuery = true; + extensions->queryCounterBitsTimeElapsed = 64; + extensions->queryCounterBitsTimestamp = + 0; // Timestamps cannot be supported due to D3D11 limitations extensions->robustness = true; extensions->blendMinMax = true; extensions->framebufferBlit = GetFramebufferBlitSupport(featureLevel); @@ -1217,6 +1223,8 @@ void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, cons extensions->unpackSubimage = true; extensions->packSubimage = true; extensions->vertexArrayObject = true; + extensions->noError = true; + extensions->lossyETCDecode = true; // D3D11 Feature Level 10_0+ uses SV_IsFrontFace in HLSL to emulate gl_FrontFacing. // D3D11 Feature Level 9_3 doesn't support SV_IsFrontFace, and has no equivalent, so can't support gl_FrontFacing. @@ -1246,11 +1254,77 @@ void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, cons #endif } -} +} // namespace d3d11_gl namespace d3d11 { +ANGLED3D11DeviceType GetDeviceType(ID3D11Device *device) +{ + // Note that this function returns an ANGLED3D11DeviceType rather than a D3D_DRIVER_TYPE value, + // since it is difficult to tell Software and Reference devices apart + + IDXGIDevice *dxgiDevice = nullptr; + IDXGIAdapter *dxgiAdapter = nullptr; + IDXGIAdapter2 *dxgiAdapter2 = nullptr; + + ANGLED3D11DeviceType retDeviceType = ANGLE_D3D11_DEVICE_TYPE_UNKNOWN; + + HRESULT hr = device->QueryInterface(__uuidof(IDXGIDevice), (void **)&dxgiDevice); + if (SUCCEEDED(hr)) + { + hr = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void **)&dxgiAdapter); + if (SUCCEEDED(hr)) + { + std::wstring adapterString; + HRESULT adapter2hr = + dxgiAdapter->QueryInterface(__uuidof(dxgiAdapter2), (void **)&dxgiAdapter2); + if (SUCCEEDED(adapter2hr)) + { + // On D3D_FEATURE_LEVEL_9_*, IDXGIAdapter::GetDesc returns "Software Adapter" + // for the description string. Try to use IDXGIAdapter2::GetDesc2 to get the + // actual hardware values if possible. + DXGI_ADAPTER_DESC2 adapterDesc2; + dxgiAdapter2->GetDesc2(&adapterDesc2); + adapterString = std::wstring(adapterDesc2.Description); + } + else + { + DXGI_ADAPTER_DESC adapterDesc; + dxgiAdapter->GetDesc(&adapterDesc); + adapterString = std::wstring(adapterDesc.Description); + } + + // Both Reference and Software adapters will be 'Software Adapter' + const bool isSoftwareDevice = + (adapterString.find(std::wstring(L"Software Adapter")) != std::string::npos); + const bool isNullDevice = (adapterString == L""); + const bool isWARPDevice = + (adapterString.find(std::wstring(L"Basic Render")) != std::string::npos); + + if (isSoftwareDevice || isNullDevice) + { + ASSERT(!isWARPDevice); + retDeviceType = ANGLE_D3D11_DEVICE_TYPE_SOFTWARE_REF_OR_NULL; + } + else if (isWARPDevice) + { + retDeviceType = ANGLE_D3D11_DEVICE_TYPE_WARP; + } + else + { + retDeviceType = ANGLE_D3D11_DEVICE_TYPE_HARDWARE; + } + } + } + + SafeRelease(dxgiDevice); + SafeRelease(dxgiAdapter); + SafeRelease(dxgiAdapter2); + + return retDeviceType; +} + void MakeValidSize(bool isImage, DXGI_FORMAT format, GLsizei *requestWidth, GLsizei *requestHeight, int *levelOffset) { const DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(format); @@ -1270,9 +1344,14 @@ void MakeValidSize(bool isImage, DXGI_FORMAT format, GLsizei *requestWidth, GLsi *levelOffset = upsampleCount; } -void GenerateInitialTextureData(GLint internalFormat, const Renderer11DeviceCaps &renderer11DeviceCaps, GLuint width, GLuint height, GLuint depth, - GLuint mipLevels, std::vector *outSubresourceData, - std::vector< std::vector > *outData) +void GenerateInitialTextureData(GLint internalFormat, + const Renderer11DeviceCaps &renderer11DeviceCaps, + GLuint width, + GLuint height, + GLuint depth, + GLuint mipLevels, + std::vector *outSubresourceData, + std::vector> *outData) { const d3d11::TextureFormat &d3dFormatInfo = d3d11::GetTextureFormatInfo(internalFormat, renderer11DeviceCaps); ASSERT(d3dFormatInfo.dataInitializerFunction != NULL); @@ -1327,13 +1406,91 @@ void SetPositionLayerTexCoord3DVertex(PositionLayerTexCoord3DVertex* vertex, flo HRESULT SetDebugName(ID3D11DeviceChild *resource, const char *name) { #if defined(_DEBUG) - return resource->SetPrivateData(WKPDID_D3DDebugObjectName, - static_cast(strlen(name)), name); + UINT existingDataSize = 0; + resource->GetPrivateData(WKPDID_D3DDebugObjectName, &existingDataSize, nullptr); + // Don't check the HRESULT- if it failed then that probably just means that no private data + // exists yet + + if (existingDataSize > 0) + { + // In some cases, ANGLE will try to apply two names to one object, which causes + // a D3D SDK Layers warning. This can occur if, for example, you 'create' two objects + // (e.g.Rasterizer States) with identical DESCs on the same device. D3D11 will optimize + // these calls and return the same object both times. + static const char *multipleNamesUsed = "Multiple names set by ANGLE"; + + // Remove the existing name + HRESULT hr = resource->SetPrivateData(WKPDID_D3DDebugObjectName, 0, nullptr); + if (FAILED(hr)) + { + return hr; + } + + // Apply the new name + return resource->SetPrivateData(WKPDID_D3DDebugObjectName, + static_cast(strlen(multipleNamesUsed)), + multipleNamesUsed); + } + else + { + return resource->SetPrivateData(WKPDID_D3DDebugObjectName, + static_cast(strlen(name)), name); + } #else return S_OK; #endif } +LazyInputLayout::LazyInputLayout(const D3D11_INPUT_ELEMENT_DESC *inputDesc, + size_t inputDescLen, + const BYTE *byteCode, + size_t byteCodeLen, + const char *debugName) + : mInputDesc(inputDescLen), + mByteCodeLen(byteCodeLen), + mByteCode(byteCode), + mDebugName(debugName) +{ + memcpy(&mInputDesc[0], inputDesc, sizeof(D3D11_INPUT_ELEMENT_DESC) * inputDescLen); +} + +ID3D11InputLayout *LazyInputLayout::resolve(ID3D11Device *device) +{ + checkAssociatedDevice(device); + + if (mResource == nullptr) + { + HRESULT result = + device->CreateInputLayout(&mInputDesc[0], static_cast(mInputDesc.size()), + mByteCode, mByteCodeLen, &mResource); + ASSERT(SUCCEEDED(result)); + UNUSED_ASSERTION_VARIABLE(result); + d3d11::SetDebugName(mResource, mDebugName); + } + + return mResource; +} + +LazyBlendState::LazyBlendState(const D3D11_BLEND_DESC &desc, const char *debugName) + : mDesc(desc), mDebugName(debugName) +{ +} + +ID3D11BlendState *LazyBlendState::resolve(ID3D11Device *device) +{ + checkAssociatedDevice(device); + + if (mResource == nullptr) + { + HRESULT result = device->CreateBlendState(&mDesc, &mResource); + ASSERT(SUCCEEDED(result)); + UNUSED_ASSERTION_VARIABLE(result); + d3d11::SetDebugName(mResource, mDebugName); + } + + return mResource; +} + WorkaroundsD3D GenerateWorkarounds(D3D_FEATURE_LEVEL featureLevel) { WorkaroundsD3D workarounds; @@ -1344,6 +1501,188 @@ WorkaroundsD3D GenerateWorkarounds(D3D_FEATURE_LEVEL featureLevel) return workarounds; } +} // namespace d3d11 + +TextureHelper11::TextureHelper11() + : mTextureType(GL_NONE), + mFormat(DXGI_FORMAT_UNKNOWN), + mSampleCount(0), + mTexture2D(nullptr), + mTexture3D(nullptr) +{ } +TextureHelper11::TextureHelper11(TextureHelper11 &&toCopy) + : mTextureType(toCopy.mTextureType), + mExtents(toCopy.mExtents), + mFormat(toCopy.mFormat), + mSampleCount(toCopy.mSampleCount), + mTexture2D(toCopy.mTexture2D), + mTexture3D(toCopy.mTexture3D) +{ + toCopy.reset(); } + +// static +TextureHelper11 TextureHelper11::MakeAndReference(ID3D11Resource *genericResource) +{ + TextureHelper11 newHelper; + newHelper.mTexture2D = d3d11::DynamicCastComObject(genericResource); + newHelper.mTexture3D = d3d11::DynamicCastComObject(genericResource); + newHelper.mTextureType = newHelper.mTexture2D ? GL_TEXTURE_2D : GL_TEXTURE_3D; + newHelper.initDesc(); + return newHelper; +} + +// static +TextureHelper11 TextureHelper11::MakeAndPossess2D(ID3D11Texture2D *texToOwn) +{ + TextureHelper11 newHelper; + newHelper.mTexture2D = texToOwn; + newHelper.mTextureType = GL_TEXTURE_2D; + newHelper.initDesc(); + return newHelper; +} + +// static +TextureHelper11 TextureHelper11::MakeAndPossess3D(ID3D11Texture3D *texToOwn) +{ + TextureHelper11 newHelper; + newHelper.mTexture3D = texToOwn; + newHelper.mTextureType = GL_TEXTURE_3D; + newHelper.initDesc(); + return newHelper; +} + +void TextureHelper11::initDesc() +{ + if (mTextureType == GL_TEXTURE_2D) + { + ASSERT(!mTexture3D); + D3D11_TEXTURE2D_DESC desc2D; + mTexture2D->GetDesc(&desc2D); + + mExtents.width = static_cast(desc2D.Width); + mExtents.height = static_cast(desc2D.Height); + mExtents.depth = 1; + mFormat = desc2D.Format; + mSampleCount = desc2D.SampleDesc.Count; + } + else + { + ASSERT(mTexture3D && mTextureType == GL_TEXTURE_3D); + D3D11_TEXTURE3D_DESC desc3D; + mTexture3D->GetDesc(&desc3D); + + mExtents.width = static_cast(desc3D.Width); + mExtents.height = static_cast(desc3D.Height); + mExtents.depth = static_cast(desc3D.Depth); + mFormat = desc3D.Format; + mSampleCount = 1; + } +} + +TextureHelper11::~TextureHelper11() +{ + SafeRelease(mTexture2D); + SafeRelease(mTexture3D); +} + +ID3D11Resource *TextureHelper11::getResource() const +{ + return mTexture2D ? static_cast(mTexture2D) + : static_cast(mTexture3D); +} + +TextureHelper11 &TextureHelper11::operator=(TextureHelper11 &&texture) +{ + SafeRelease(mTexture2D); + SafeRelease(mTexture3D); + + mTextureType = texture.mTextureType; + mExtents = texture.mExtents; + mFormat = texture.mFormat; + mSampleCount = texture.mSampleCount; + mTexture2D = texture.mTexture2D; + mTexture3D = texture.mTexture3D; + texture.reset(); + return *this; +} + +void TextureHelper11::reset() +{ + mTextureType = GL_NONE; + mExtents = gl::Extents(); + mFormat = DXGI_FORMAT_UNKNOWN; + mSampleCount = 0; + mTexture2D = nullptr; + mTexture3D = nullptr; +} + +gl::ErrorOrResult CreateStagingTexture(GLenum textureType, + DXGI_FORMAT dxgiFormat, + const gl::Extents &size, + ID3D11Device *device) +{ + if (textureType == GL_TEXTURE_2D) + { + D3D11_TEXTURE2D_DESC stagingDesc; + stagingDesc.Width = size.width; + stagingDesc.Height = size.height; + stagingDesc.MipLevels = 1; + stagingDesc.ArraySize = 1; + stagingDesc.Format = dxgiFormat; + stagingDesc.SampleDesc.Count = 1; + stagingDesc.SampleDesc.Quality = 0; + stagingDesc.Usage = D3D11_USAGE_STAGING; + stagingDesc.BindFlags = 0; + stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + stagingDesc.MiscFlags = 0; + + ID3D11Texture2D *stagingTex = nullptr; + HRESULT result = device->CreateTexture2D(&stagingDesc, nullptr, &stagingTex); + if (FAILED(result)) + { + return gl::Error(GL_OUT_OF_MEMORY, "CreateStagingTextureFor failed, HRESULT: 0x%X.", + result); + } + + return TextureHelper11::MakeAndPossess2D(stagingTex); + } + ASSERT(textureType == GL_TEXTURE_3D); + + D3D11_TEXTURE3D_DESC stagingDesc; + stagingDesc.Width = size.width; + stagingDesc.Height = size.height; + stagingDesc.Depth = 1; + stagingDesc.MipLevels = 1; + stagingDesc.Format = dxgiFormat; + stagingDesc.Usage = D3D11_USAGE_STAGING; + stagingDesc.BindFlags = 0; + stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + stagingDesc.MiscFlags = 0; + + ID3D11Texture3D *stagingTex = nullptr; + HRESULT result = device->CreateTexture3D(&stagingDesc, nullptr, &stagingTex); + if (FAILED(result)) + { + return gl::Error(GL_OUT_OF_MEMORY, "CreateStagingTextureFor failed, HRESULT: 0x%X.", + result); + } + + return TextureHelper11::MakeAndPossess3D(stagingTex); +} + +bool UsePresentPathFast(const Renderer11 *renderer, + const gl::FramebufferAttachment *framebufferAttachment) +{ + if (framebufferAttachment == nullptr) + { + return false; + } + + return (framebufferAttachment->type() == GL_FRAMEBUFFER_DEFAULT && + renderer->presentPathFastEnabled()); +} + +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h index 6d6591e45c..4925a2d227 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h @@ -10,13 +10,14 @@ #ifndef LIBANGLE_RENDERER_D3D_D3D11_RENDERER11_UTILS_H_ #define LIBANGLE_RENDERER_D3D_D3D11_RENDERER11_UTILS_H_ +#include +#include + #include "libANGLE/angletypes.h" #include "libANGLE/Caps.h" #include "libANGLE/Error.h" #include "libANGLE/renderer/d3d/RendererD3D.h" -#include - namespace gl { class FramebufferAttachment; @@ -24,10 +25,13 @@ class FramebufferAttachment; namespace rx { +class Renderer11; class RenderTarget11; struct WorkaroundsD3D; struct Renderer11DeviceCaps; +using RenderTargetArray = std::array; + namespace gl_d3d11 { @@ -47,7 +51,7 @@ D3D11_TEXTURE_ADDRESS_MODE ConvertTextureWrap(GLenum wrap); D3D11_QUERY ConvertQueryType(GLenum queryType); -} +} // namespace gl_d3d11 namespace d3d11_gl { @@ -60,16 +64,31 @@ GLint GetMaximumClientVersion(D3D_FEATURE_LEVEL featureLevel); void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, const Renderer11DeviceCaps &renderer11DeviceCaps, gl::Caps *caps, gl::TextureCapsMap *textureCapsMap, gl::Extensions *extensions, gl::Limitations *limitations); -} +} // namespace d3d11_gl namespace d3d11 { +enum ANGLED3D11DeviceType +{ + ANGLE_D3D11_DEVICE_TYPE_UNKNOWN, + ANGLE_D3D11_DEVICE_TYPE_HARDWARE, + ANGLE_D3D11_DEVICE_TYPE_SOFTWARE_REF_OR_NULL, + ANGLE_D3D11_DEVICE_TYPE_WARP, +}; + +ANGLED3D11DeviceType GetDeviceType(ID3D11Device *device); + void MakeValidSize(bool isImage, DXGI_FORMAT format, GLsizei *requestWidth, GLsizei *requestHeight, int *levelOffset); -void GenerateInitialTextureData(GLint internalFormat, const Renderer11DeviceCaps &renderer11DeviceCaps, GLuint width, GLuint height, GLuint depth, - GLuint mipLevels, std::vector *outSubresourceData, - std::vector< std::vector > *outData); +void GenerateInitialTextureData(GLint internalFormat, + const Renderer11DeviceCaps &renderer11DeviceCaps, + GLuint width, + GLuint height, + GLuint depth, + GLuint mipLevels, + std::vector *outSubresourceData, + std::vector> *outData); UINT GetPrimitiveRestartIndex(); @@ -296,35 +315,17 @@ class LazyInputLayout final : public LazyResource const char *mDebugName; }; -inline LazyInputLayout::LazyInputLayout( - const D3D11_INPUT_ELEMENT_DESC *inputDesc, - size_t inputDescLen, - const BYTE *byteCode, - size_t byteCodeLen, - const char *debugName) - : mInputDesc(inputDescLen), - mByteCodeLen(byteCodeLen), - mByteCode(byteCode), - mDebugName(debugName) +class LazyBlendState final : public LazyResource { - memcpy(&mInputDesc[0], inputDesc, sizeof(D3D11_INPUT_ELEMENT_DESC) * inputDescLen); -} + public: + LazyBlendState(const D3D11_BLEND_DESC &desc, const char *debugName); -inline ID3D11InputLayout *LazyInputLayout::resolve(ID3D11Device *device) -{ - checkAssociatedDevice(device); + ID3D11BlendState *resolve(ID3D11Device *device) override; - if (mResource == nullptr) - { - HRESULT result = device->CreateInputLayout( - &mInputDesc[0], static_cast(mInputDesc.size()), mByteCode, mByteCodeLen, &mResource); - ASSERT(SUCCEEDED(result)); - UNUSED_ASSERTION_VARIABLE(result); - d3d11::SetDebugName(mResource, mDebugName); - } - - return mResource; -} + private: + D3D11_BLEND_DESC mDesc; + const char *mDebugName; +}; // Copy data to small D3D11 buffers, such as for small constant buffers, which use one struct to // represent an entire buffer. @@ -342,8 +343,48 @@ void SetBufferData(ID3D11DeviceContext *context, ID3D11Buffer *constantBuffer, c } WorkaroundsD3D GenerateWorkarounds(D3D_FEATURE_LEVEL featureLevel); -} +} // namespace d3d11 -} +// A helper class which wraps a 2D or 3D texture. +class TextureHelper11 : angle::NonCopyable +{ + public: + TextureHelper11(); + TextureHelper11(TextureHelper11 &&toCopy); + ~TextureHelper11(); + TextureHelper11 &operator=(TextureHelper11 &&texture); + + static TextureHelper11 MakeAndReference(ID3D11Resource *genericResource); + static TextureHelper11 MakeAndPossess2D(ID3D11Texture2D *texToOwn); + static TextureHelper11 MakeAndPossess3D(ID3D11Texture3D *texToOwn); + + GLenum getTextureType() const { return mTextureType; } + gl::Extents getExtents() const { return mExtents; } + DXGI_FORMAT getFormat() const { return mFormat; } + int getSampleCount() const { return mSampleCount; } + ID3D11Texture2D *getTexture2D() const { return mTexture2D; } + ID3D11Texture3D *getTexture3D() const { return mTexture3D; } + ID3D11Resource *getResource() const; + + private: + void reset(); + void initDesc(); + + GLenum mTextureType; + gl::Extents mExtents; + DXGI_FORMAT mFormat; + int mSampleCount; + ID3D11Texture2D *mTexture2D; + ID3D11Texture3D *mTexture3D; +}; + +gl::ErrorOrResult CreateStagingTexture(GLenum textureType, + DXGI_FORMAT dxgiFormat, + const gl::Extents &size, + ID3D11Device *device); + +bool UsePresentPathFast(const Renderer11 *renderer, const gl::FramebufferAttachment *colorbuffer); + +} // namespace rx #endif // LIBANGLE_RENDERER_D3D_D3D11_RENDERER11_UTILS_H_ diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_data.json b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_data.json index ca84a00456..87d303437f 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_data.json +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_data.json @@ -4,14 +4,12 @@ "texFormat": "DXGI_FORMAT_A8_UNORM", "srvFormat": "DXGI_FORMAT_A8_UNORM", "rtvFormat": "DXGI_FORMAT_A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL10Plus" }, { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL9_3" } ], @@ -19,18 +17,14 @@ { "texFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", "srvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", - "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT" } ], "GL_ALPHA32F_EXT": [ { "texFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", "srvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", - "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT" } ], "GL_ALPHA8_EXT": [ @@ -38,14 +32,12 @@ "texFormat": "DXGI_FORMAT_A8_UNORM", "srvFormat": "DXGI_FORMAT_A8_UNORM", "rtvFormat": "DXGI_FORMAT_A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL10Plus" }, { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL9_3" } ], @@ -53,44 +45,34 @@ { "texFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", "srvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM" } ], "GL_BGRA4_ANGLEX": [ { "texFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", "srvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM" } ], "GL_BGRA8_EXT": [ { "texFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", "srvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM" } ], "GL_BGRA_EXT": [ { "texFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", "srvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM" } ], "GL_COMPRESSED_R11_EAC": [ { "texFormat": "DXGI_FORMAT_R8_UNORM", "srvFormat": "DXGI_FORMAT_R8_UNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL10Plus" } ], @@ -98,8 +80,6 @@ { "texFormat": "DXGI_FORMAT_R8G8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8_UNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL10Plus" } ], @@ -107,8 +87,6 @@ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL10Plus" } ], @@ -116,8 +94,6 @@ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL10Plus" } ], @@ -125,53 +101,37 @@ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL10Plus" } ], "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT": [ { "texFormat": "DXGI_FORMAT_BC1_UNORM", - "srvFormat": "DXGI_FORMAT_BC1_UNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "srvFormat": "DXGI_FORMAT_BC1_UNORM" } ], "GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE": [ { "texFormat": "DXGI_FORMAT_BC2_UNORM", - "srvFormat": "DXGI_FORMAT_BC2_UNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "srvFormat": "DXGI_FORMAT_BC2_UNORM" } ], "GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE": [ { "texFormat": "DXGI_FORMAT_BC3_UNORM", - "srvFormat": "DXGI_FORMAT_BC3_UNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "srvFormat": "DXGI_FORMAT_BC3_UNORM" } ], "GL_COMPRESSED_RGB_S3TC_DXT1_EXT": [ { "texFormat": "DXGI_FORMAT_BC1_UNORM", - "srvFormat": "DXGI_FORMAT_BC1_UNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "srvFormat": "DXGI_FORMAT_BC1_UNORM" } ], "GL_COMPRESSED_SIGNED_R11_EAC": [ { "texFormat": "DXGI_FORMAT_R8_SNORM", "srvFormat": "DXGI_FORMAT_R8_SNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL10Plus" } ], @@ -179,8 +139,6 @@ { "texFormat": "DXGI_FORMAT_R8G8_SNORM", "srvFormat": "DXGI_FORMAT_R8G8_SNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL10Plus" } ], @@ -188,8 +146,6 @@ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL10Plus" } ], @@ -197,8 +153,6 @@ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL10Plus" } ], @@ -206,8 +160,6 @@ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL10Plus" } ], @@ -215,14 +167,11 @@ { "texFormat": "DXGI_FORMAT_R24G8_TYPELESS", "srvFormat": "DXGI_FORMAT_R24_UNORM_X8_TYPELESS", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", "dsvFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", "requirementsFcn": "OnlyFL10Plus" }, { "texFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", - "srvFormat": "DXGI_FORMAT_UNKNOWN", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", "dsvFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", "requirementsFcn": "OnlyFL9_3" } @@ -231,15 +180,10 @@ { "texFormat": "DXGI_FORMAT_R32G8X24_TYPELESS", "srvFormat": "DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", "dsvFormat": "DXGI_FORMAT_D32_FLOAT_S8X24_UINT", "requirementsFcn": "OnlyFL10Plus" }, { - "texFormat": "DXGI_FORMAT_UNKNOWN", - "srvFormat": "DXGI_FORMAT_UNKNOWN", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL9_3" } ], @@ -247,14 +191,11 @@ { "texFormat": "DXGI_FORMAT_R16_TYPELESS", "srvFormat": "DXGI_FORMAT_R16_UNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", "dsvFormat": "DXGI_FORMAT_D16_UNORM", "requirementsFcn": "OnlyFL10Plus" }, { "texFormat": "DXGI_FORMAT_D16_UNORM", - "srvFormat": "DXGI_FORMAT_UNKNOWN", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", "dsvFormat": "DXGI_FORMAT_D16_UNORM", "requirementsFcn": "OnlyFL9_3" } @@ -263,14 +204,11 @@ { "texFormat": "DXGI_FORMAT_R24G8_TYPELESS", "srvFormat": "DXGI_FORMAT_R24_UNORM_X8_TYPELESS", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", "dsvFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", "requirementsFcn": "OnlyFL10Plus" }, { "texFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", - "srvFormat": "DXGI_FORMAT_UNKNOWN", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", "dsvFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", "requirementsFcn": "OnlyFL9_3" } @@ -279,15 +217,10 @@ { "texFormat": "DXGI_FORMAT_R32_TYPELESS", "srvFormat": "DXGI_FORMAT_R32_FLOAT", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", "dsvFormat": "DXGI_FORMAT_D32_FLOAT", "requirementsFcn": "OnlyFL10Plus" }, { - "texFormat": "DXGI_FORMAT_UNKNOWN", - "srvFormat": "DXGI_FORMAT_UNKNOWN", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "OnlyFL9_3" } ], @@ -295,7 +228,6 @@ { "texFormat": "DXGI_FORMAT_R24G8_TYPELESS", "srvFormat": "DXGI_FORMAT_R24_UNORM_X8_TYPELESS", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", "dsvFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", "requirementsFcn": "OnlyFL10Plus" } @@ -303,361 +235,281 @@ "GL_ETC1_RGB8_OES": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM" + } + ], + "GL_ETC1_RGB8_LOSSY_DECODE_ANGLE": [ + { + "texFormat": "DXGI_FORMAT_BC1_UNORM", + "srvFormat": "DXGI_FORMAT_BC1_UNORM" } ], "GL_LUMINANCE": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM" } ], "GL_LUMINANCE16F_EXT": [ { "texFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", "srvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", - "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT" } ], "GL_LUMINANCE32F_EXT": [ { "texFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", "srvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", - "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT" } ], "GL_LUMINANCE8_ALPHA8_EXT": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM" } ], "GL_LUMINANCE8_EXT": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM" } ], "GL_LUMINANCE_ALPHA": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM" } ], "GL_LUMINANCE_ALPHA16F_EXT": [ { "texFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", "srvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", - "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT" } ], "GL_LUMINANCE_ALPHA32F_EXT": [ { "texFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", "srvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", - "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT" } ], "GL_NONE": [ { - "texFormat": "DXGI_FORMAT_UNKNOWN", - "srvFormat": "DXGI_FORMAT_UNKNOWN", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" } ], "GL_R11F_G11F_B10F": [ { "texFormat": "DXGI_FORMAT_R11G11B10_FLOAT", "srvFormat": "DXGI_FORMAT_R11G11B10_FLOAT", - "rtvFormat": "DXGI_FORMAT_R11G11B10_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R11G11B10_FLOAT" } ], "GL_R16F": [ { "texFormat": "DXGI_FORMAT_R16_FLOAT", "srvFormat": "DXGI_FORMAT_R16_FLOAT", - "rtvFormat": "DXGI_FORMAT_R16_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16_FLOAT" } ], "GL_R16I": [ { "texFormat": "DXGI_FORMAT_R16_SINT", "srvFormat": "DXGI_FORMAT_R16_SINT", - "rtvFormat": "DXGI_FORMAT_R16_SINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16_SINT" } ], "GL_R16UI": [ { "texFormat": "DXGI_FORMAT_R16_UINT", "srvFormat": "DXGI_FORMAT_R16_UINT", - "rtvFormat": "DXGI_FORMAT_R16_UINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16_UINT" } ], "GL_R32F": [ { "texFormat": "DXGI_FORMAT_R32_FLOAT", "srvFormat": "DXGI_FORMAT_R32_FLOAT", - "rtvFormat": "DXGI_FORMAT_R32_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32_FLOAT" } ], "GL_R32I": [ { "texFormat": "DXGI_FORMAT_R32_SINT", "srvFormat": "DXGI_FORMAT_R32_SINT", - "rtvFormat": "DXGI_FORMAT_R32_SINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32_SINT" } ], "GL_R32UI": [ { "texFormat": "DXGI_FORMAT_R32_UINT", "srvFormat": "DXGI_FORMAT_R32_UINT", - "rtvFormat": "DXGI_FORMAT_R32_UINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32_UINT" } ], "GL_R8": [ { "texFormat": "DXGI_FORMAT_R8_UNORM", "srvFormat": "DXGI_FORMAT_R8_UNORM", - "rtvFormat": "DXGI_FORMAT_R8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8_UNORM" } ], "GL_R8I": [ { "texFormat": "DXGI_FORMAT_R8_SINT", "srvFormat": "DXGI_FORMAT_R8_SINT", - "rtvFormat": "DXGI_FORMAT_R8_SINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8_SINT" } ], "GL_R8UI": [ { "texFormat": "DXGI_FORMAT_R8_UINT", "srvFormat": "DXGI_FORMAT_R8_UINT", - "rtvFormat": "DXGI_FORMAT_R8_UINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8_UINT" } ], "GL_R8_SNORM": [ { "texFormat": "DXGI_FORMAT_R8_SNORM", - "srvFormat": "DXGI_FORMAT_R8_SNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "srvFormat": "DXGI_FORMAT_R8_SNORM" } ], "GL_RG16F": [ { "texFormat": "DXGI_FORMAT_R16G16_FLOAT", "srvFormat": "DXGI_FORMAT_R16G16_FLOAT", - "rtvFormat": "DXGI_FORMAT_R16G16_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16G16_FLOAT" } ], "GL_RG16I": [ { "texFormat": "DXGI_FORMAT_R16G16_SINT", "srvFormat": "DXGI_FORMAT_R16G16_SINT", - "rtvFormat": "DXGI_FORMAT_R16G16_SINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16G16_SINT" } ], "GL_RG16UI": [ { "texFormat": "DXGI_FORMAT_R16G16_UINT", "srvFormat": "DXGI_FORMAT_R16G16_UINT", - "rtvFormat": "DXGI_FORMAT_R16G16_UINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16G16_UINT" } ], "GL_RG32F": [ { "texFormat": "DXGI_FORMAT_R32G32_FLOAT", "srvFormat": "DXGI_FORMAT_R32G32_FLOAT", - "rtvFormat": "DXGI_FORMAT_R32G32_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32G32_FLOAT" } ], "GL_RG32I": [ { "texFormat": "DXGI_FORMAT_R32G32_SINT", "srvFormat": "DXGI_FORMAT_R32G32_SINT", - "rtvFormat": "DXGI_FORMAT_R32G32_SINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32G32_SINT" } ], "GL_RG32UI": [ { "texFormat": "DXGI_FORMAT_R32G32_UINT", "srvFormat": "DXGI_FORMAT_R32G32_UINT", - "rtvFormat": "DXGI_FORMAT_R32G32_UINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32G32_UINT" } ], "GL_RG8": [ { "texFormat": "DXGI_FORMAT_R8G8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8_UNORM", - "rtvFormat": "DXGI_FORMAT_R8G8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8_UNORM" } ], "GL_RG8I": [ { "texFormat": "DXGI_FORMAT_R8G8_SINT", "srvFormat": "DXGI_FORMAT_R8G8_SINT", - "rtvFormat": "DXGI_FORMAT_R8G8_SINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8_SINT" } ], "GL_RG8UI": [ { "texFormat": "DXGI_FORMAT_R8G8_UINT", "srvFormat": "DXGI_FORMAT_R8G8_UINT", - "rtvFormat": "DXGI_FORMAT_R8G8_UINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8_UINT" } ], "GL_RG8_SNORM": [ { "texFormat": "DXGI_FORMAT_R8G8_SNORM", - "srvFormat": "DXGI_FORMAT_R8G8_SNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "srvFormat": "DXGI_FORMAT_R8G8_SNORM" } ], "GL_RGB": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM" } ], "GL_RGB10_A2": [ { "texFormat": "DXGI_FORMAT_R10G10B10A2_UNORM", "srvFormat": "DXGI_FORMAT_R10G10B10A2_UNORM", - "rtvFormat": "DXGI_FORMAT_R10G10B10A2_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R10G10B10A2_UNORM" } ], "GL_RGB10_A2UI": [ { "texFormat": "DXGI_FORMAT_R10G10B10A2_UINT", "srvFormat": "DXGI_FORMAT_R10G10B10A2_UINT", - "rtvFormat": "DXGI_FORMAT_R10G10B10A2_UINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R10G10B10A2_UINT" } ], "GL_RGB16F": [ { "texFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", "srvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", - "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT" } ], "GL_RGB16I": [ { "texFormat": "DXGI_FORMAT_R16G16B16A16_SINT", "srvFormat": "DXGI_FORMAT_R16G16B16A16_SINT", - "rtvFormat": "DXGI_FORMAT_R16G16B16A16_SINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_SINT" } ], "GL_RGB16UI": [ { "texFormat": "DXGI_FORMAT_R16G16B16A16_UINT", "srvFormat": "DXGI_FORMAT_R16G16B16A16_UINT", - "rtvFormat": "DXGI_FORMAT_R16G16B16A16_UINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_UINT" } ], "GL_RGB32F": [ { "texFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", "srvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", - "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT" } ], "GL_RGB32I": [ { "texFormat": "DXGI_FORMAT_R32G32B32A32_SINT", "srvFormat": "DXGI_FORMAT_R32G32B32A32_SINT", - "rtvFormat": "DXGI_FORMAT_R32G32B32A32_SINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_SINT" } ], "GL_RGB32UI": [ { "texFormat": "DXGI_FORMAT_R32G32B32A32_UINT", "srvFormat": "DXGI_FORMAT_R32G32B32A32_UINT", - "rtvFormat": "DXGI_FORMAT_R32G32B32A32_UINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_UINT" } ], "GL_RGB565": [ @@ -665,14 +517,12 @@ "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "SupportsFormat" }, { "texFormat": "DXGI_FORMAT_B5G6R5_UNORM", "srvFormat": "DXGI_FORMAT_B5G6R5_UNORM", "rtvFormat": "DXGI_FORMAT_B5G6R5_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "SupportsFormat" } ], @@ -681,14 +531,12 @@ "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "SupportsFormat" }, { "texFormat": "DXGI_FORMAT_B5G5R5A1_UNORM", "srvFormat": "DXGI_FORMAT_B5G5R5A1_UNORM", "rtvFormat": "DXGI_FORMAT_B5G5R5A1_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "SupportsFormat" } ], @@ -696,108 +544,82 @@ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM" } ], "GL_RGB8I": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_SINT", "srvFormat": "DXGI_FORMAT_R8G8B8A8_SINT", - "rtvFormat": "DXGI_FORMAT_R8G8B8A8_SINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_SINT" } ], "GL_RGB8UI": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UINT", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UINT", - "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UINT" } ], "GL_RGB8_SNORM": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_SNORM", - "srvFormat": "DXGI_FORMAT_R8G8B8A8_SNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "srvFormat": "DXGI_FORMAT_R8G8B8A8_SNORM" } ], "GL_RGB9_E5": [ { "texFormat": "DXGI_FORMAT_R9G9B9E5_SHAREDEXP", - "srvFormat": "DXGI_FORMAT_R9G9B9E5_SHAREDEXP", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "srvFormat": "DXGI_FORMAT_R9G9B9E5_SHAREDEXP" } ], "GL_RGBA": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM" } ], "GL_RGBA16F": [ { "texFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", "srvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", - "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT" } ], "GL_RGBA16I": [ { "texFormat": "DXGI_FORMAT_R16G16B16A16_SINT", "srvFormat": "DXGI_FORMAT_R16G16B16A16_SINT", - "rtvFormat": "DXGI_FORMAT_R16G16B16A16_SINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_SINT" } ], "GL_RGBA16UI": [ { "texFormat": "DXGI_FORMAT_R16G16B16A16_UINT", "srvFormat": "DXGI_FORMAT_R16G16B16A16_UINT", - "rtvFormat": "DXGI_FORMAT_R16G16B16A16_UINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_UINT" } ], "GL_RGBA32F": [ { "texFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", "srvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", - "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT" } ], "GL_RGBA32I": [ { "texFormat": "DXGI_FORMAT_R32G32B32A32_SINT", "srvFormat": "DXGI_FORMAT_R32G32B32A32_SINT", - "rtvFormat": "DXGI_FORMAT_R32G32B32A32_SINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_SINT" } ], "GL_RGBA32UI": [ { "texFormat": "DXGI_FORMAT_R32G32B32A32_UINT", "srvFormat": "DXGI_FORMAT_R32G32B32A32_UINT", - "rtvFormat": "DXGI_FORMAT_R32G32B32A32_UINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_UINT" } ], "GL_RGBA4": [ @@ -805,14 +627,12 @@ "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "SupportsFormat" }, { "texFormat": "DXGI_FORMAT_B4G4R4A4_UNORM", "srvFormat": "DXGI_FORMAT_B4G4R4A4_UNORM", "rtvFormat": "DXGI_FORMAT_B4G4R4A4_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", "requirementsFcn": "SupportsFormat" } ], @@ -820,70 +640,53 @@ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM" } ], "GL_RGBA8I": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_SINT", "srvFormat": "DXGI_FORMAT_R8G8B8A8_SINT", - "rtvFormat": "DXGI_FORMAT_R8G8B8A8_SINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_SINT" } ], "GL_RGBA8UI": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UINT", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UINT", - "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UINT", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UINT" } ], "GL_RGBA8_SNORM": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_SNORM", - "srvFormat": "DXGI_FORMAT_R8G8B8A8_SNORM", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "srvFormat": "DXGI_FORMAT_R8G8B8A8_SNORM" } ], "GL_SRGB8": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", - "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB" } ], "GL_SRGB8_ALPHA8": [ { "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", - "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", - "dsvFormat": "DXGI_FORMAT_UNKNOWN", - "requirementsFcn": "AnyDevice" + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB" } ], "GL_STENCIL_INDEX8": [ { "texFormat": "DXGI_FORMAT_R24G8_TYPELESS", "srvFormat": "DXGI_FORMAT_X24_TYPELESS_G8_UINT", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", "dsvFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", "requirementsFcn": "OnlyFL10Plus" }, { "texFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", - "srvFormat": "DXGI_FORMAT_UNKNOWN", - "rtvFormat": "DXGI_FORMAT_UNKNOWN", "dsvFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", "requirementsFcn": "OnlyFL9_3" } ] -} \ No newline at end of file +} diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.cpp deleted file mode 100644 index c37a89150a..0000000000 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.cpp +++ /dev/null @@ -1,414 +0,0 @@ - -// -// Copyright 2015 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. -// -// texture_format_table: -// Queries for full textureFormat information based in internalFormat -// - -#include "libANGLE/renderer/d3d/d3d11/texture_format_table.h" - -#include "libANGLE/renderer/d3d/d3d11/formatutils11.h" -#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h" -#include "libANGLE/renderer/d3d/d3d11/swizzle_format_info.h" -#include "libANGLE/renderer/d3d/d3d11/texture_format_util.h" -#include "libANGLE/renderer/d3d/loadimage.h" - -namespace rx -{ - -namespace d3d11 -{ - -namespace -{ - -typedef bool (*FormatSupportFunction)(const Renderer11DeviceCaps &); - -bool AnyDevice(const Renderer11DeviceCaps &deviceCaps) -{ - return true; -} - -bool OnlyFL10Plus(const Renderer11DeviceCaps &deviceCaps) -{ - return (deviceCaps.featureLevel >= D3D_FEATURE_LEVEL_10_0); -} - -bool OnlyFL9_3(const Renderer11DeviceCaps &deviceCaps) -{ - return (deviceCaps.featureLevel == D3D_FEATURE_LEVEL_9_3); -} - -template -bool SupportsFormat(const Renderer11DeviceCaps &deviceCaps) -{ - // Must support texture, SRV and RTV support - UINT mustSupport = D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_TEXTURECUBE | - D3D11_FORMAT_SUPPORT_SHADER_SAMPLE | D3D11_FORMAT_SUPPORT_MIP | - D3D11_FORMAT_SUPPORT_RENDER_TARGET; - - if (d3d11_gl::GetMaximumClientVersion(deviceCaps.featureLevel) > 2) - { - mustSupport |= D3D11_FORMAT_SUPPORT_TEXTURE3D; - } - - bool fullSupport = false; - if (format == DXGI_FORMAT_B5G6R5_UNORM) - { - // All hardware that supports DXGI_FORMAT_B5G6R5_UNORM should support autogen mipmaps, but - // check anyway. - mustSupport |= D3D11_FORMAT_SUPPORT_MIP_AUTOGEN; - fullSupport = ((deviceCaps.B5G6R5support & mustSupport) == mustSupport); - } - else if (format == DXGI_FORMAT_B4G4R4A4_UNORM) - { - fullSupport = ((deviceCaps.B4G4R4A4support & mustSupport) == mustSupport); - } - else if (format == DXGI_FORMAT_B5G5R5A1_UNORM) - { - fullSupport = ((deviceCaps.B5G5R5A1support & mustSupport) == mustSupport); - } - else - { - UNREACHABLE(); - return false; - } - - // This 'SupportsFormat' function is used by individual entries in the D3D11 Format Map below, - // which maps GL formats to DXGI formats. - if (requireSupport) - { - // This means that ANGLE would like to use the entry in the map if the inputted DXGI format - // *IS* supported. - // e.g. the entry might map GL_RGB5_A1 to DXGI_FORMAT_B5G5R5A1, which should only be used if - // DXGI_FORMAT_B5G5R5A1 is supported. - // In this case, we should only return 'true' if the format *IS* supported. - return fullSupport; - } - else - { - // This means that ANGLE would like to use the entry in the map if the inputted DXGI format - // *ISN'T* supported. - // This might be a fallback entry. e.g. for ANGLE to use DXGI_FORMAT_R8G8B8A8_UNORM if - // DXGI_FORMAT_B5G5R5A1 isn't supported. - // In this case, we should only return 'true' if the format *ISN'T* supported. - return !fullSupport; - } -} - -// End Format Support Functions - -// For sized GL internal formats, there are several possible corresponding D3D11 formats depending -// on device capabilities. -// This map type allows querying for the DXGI texture formats to use for textures, SRVs, RTVs and -// DSVs given a GL internal format. -typedef std::pair TextureFormatWithSupportFunction; -typedef std::map> D3D11ES3FormatMap; - -inline void InsertD3D11FormatInfo(D3D11ES3FormatMap *formatMap, - GLenum internalFormat, - DXGI_FORMAT texFormat, - DXGI_FORMAT srvFormat, - DXGI_FORMAT rtvFormat, - DXGI_FORMAT dsvFormat, - FormatSupportFunction formatSupportFunction) -{ - TextureFormat info; - info.texFormat = texFormat; - info.srvFormat = srvFormat; - info.rtvFormat = rtvFormat; - info.dsvFormat = dsvFormat; - - // Given a GL internal format, the renderFormat is the DSV format if it is depth- or - // stencil-renderable, - // the RTV format if it is color-renderable, and the (nonrenderable) texture format otherwise. - if (dsvFormat != DXGI_FORMAT_UNKNOWN) - { - info.renderFormat = dsvFormat; - } - else if (rtvFormat != DXGI_FORMAT_UNKNOWN) - { - info.renderFormat = rtvFormat; - } - else if (texFormat != DXGI_FORMAT_UNKNOWN) - { - info.renderFormat = texFormat; - } - else - { - info.renderFormat = DXGI_FORMAT_UNKNOWN; - } - - // Compute the swizzle formats - const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat); - if (internalFormat != GL_NONE && formatInfo.pixelBytes > 0) - { - if (formatInfo.componentCount != 4 || texFormat == DXGI_FORMAT_UNKNOWN || - srvFormat == DXGI_FORMAT_UNKNOWN || rtvFormat == DXGI_FORMAT_UNKNOWN) - { - // Get the maximum sized component - unsigned int maxBits = 1; - if (formatInfo.compressed) - { - unsigned int compressedBitsPerBlock = formatInfo.pixelBytes * 8; - unsigned int blockSize = - formatInfo.compressedBlockWidth * formatInfo.compressedBlockHeight; - maxBits = std::max(compressedBitsPerBlock / blockSize, maxBits); - } - else - { - maxBits = std::max(maxBits, formatInfo.alphaBits); - maxBits = std::max(maxBits, formatInfo.redBits); - maxBits = std::max(maxBits, formatInfo.greenBits); - maxBits = std::max(maxBits, formatInfo.blueBits); - maxBits = std::max(maxBits, formatInfo.luminanceBits); - maxBits = std::max(maxBits, formatInfo.depthBits); - } - - maxBits = roundUp(maxBits, 8U); - - static const SwizzleInfoMap &swizzleMap = BuildSwizzleInfoMap(); - SwizzleInfoMap::const_iterator swizzleIter = - swizzleMap.find(SwizzleSizeType(maxBits, formatInfo.componentType)); - - // Handle the odd case where we might find an invalid swizzle key. - // This should never happen, but it's hard to prove, so add a fail-safe. - ASSERT(swizzleIter != swizzleMap.end()); - if (swizzleIter != swizzleMap.end()) - { - const SwizzleFormatInfo &swizzleInfo = swizzleIter->second; - info.swizzleTexFormat = swizzleInfo.mTexFormat; - info.swizzleSRVFormat = swizzleInfo.mSRVFormat; - info.swizzleRTVFormat = swizzleInfo.mRTVFormat; - } - } - else - { - // The original texture format is suitable for swizzle operations - info.swizzleTexFormat = texFormat; - info.swizzleSRVFormat = srvFormat; - info.swizzleRTVFormat = rtvFormat; - } - } - else - { - // Not possible to swizzle with this texture format since it is either unsized or GL_NONE - info.swizzleTexFormat = DXGI_FORMAT_UNKNOWN; - info.swizzleSRVFormat = DXGI_FORMAT_UNKNOWN; - info.swizzleRTVFormat = DXGI_FORMAT_UNKNOWN; - } - - // Check if there is an initialization function for this texture format - static const InternalFormatInitializerMap &initializerMap = BuildInternalFormatInitializerMap(); - auto initializerIter = - initializerMap.find(InitializeTextureFormatPair(internalFormat, texFormat)); - info.dataInitializerFunction = - (initializerIter != initializerMap.end()) ? initializerIter->second : NULL; - - // Gather all the load functions for this internal format - static const D3D11LoadFunctionMap &loadFunctions = BuildD3D11LoadFunctionMap(); - auto loadFunctionIter = loadFunctions.find(internalFormat); - if (loadFunctionIter != loadFunctions.end()) - { - const std::vector &loadFunctionVector = loadFunctionIter->second; - for (size_t i = 0; i < loadFunctionVector.size(); i++) - { - DxgiFormatLoadFunctionPair formatFuncPair = loadFunctionVector[i].second; - GLenum type = loadFunctionVector[i].first; - DXGI_FORMAT dxgiFormat = formatFuncPair.first; - rx::LoadImageFunction loadFunc = formatFuncPair.second; - - if (dxgiFormat == texFormat || dxgiFormat == DXGI_FORMAT_UNKNOWN) - { - info.loadFunctions.insert(std::make_pair(type, loadFunc)); - } - } - } - - ASSERT(info.loadFunctions.size() != 0 || internalFormat == GL_NONE); - - (*formatMap)[internalFormat].push_back(std::make_pair(formatSupportFunction, info)); -} - -const D3D11ES3FormatMap &BuildD3D11FormatMap() -{ - static D3D11ES3FormatMap map; - - // clang-format off - // | GL internal format | D3D11 texture format | D3D11 SRV format | D3D11 RTV format | D3D11 DSV format | Requirements - InsertD3D11FormatInfo(&map, GL_NONE, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_R8, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_R8_SNORM, DXGI_FORMAT_R8_SNORM, DXGI_FORMAT_R8_SNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RG8, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RG8_SNORM, DXGI_FORMAT_R8G8_SNORM, DXGI_FORMAT_R8G8_SNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB8, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB8_SNORM, DXGI_FORMAT_R8G8B8A8_SNORM, DXGI_FORMAT_R8G8B8A8_SNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB565, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, SupportsFormat); - InsertD3D11FormatInfo(&map, GL_RGB565, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_UNKNOWN, SupportsFormat); - InsertD3D11FormatInfo(&map, GL_RGBA4, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, SupportsFormat); - InsertD3D11FormatInfo(&map, GL_RGBA4, DXGI_FORMAT_B4G4R4A4_UNORM, DXGI_FORMAT_B4G4R4A4_UNORM, DXGI_FORMAT_B4G4R4A4_UNORM, DXGI_FORMAT_UNKNOWN, SupportsFormat); - InsertD3D11FormatInfo(&map, GL_RGB5_A1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, SupportsFormat); - InsertD3D11FormatInfo(&map, GL_RGB5_A1, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_UNKNOWN, SupportsFormat); - InsertD3D11FormatInfo(&map, GL_RGBA8, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGBA8_SNORM, DXGI_FORMAT_R8G8B8A8_SNORM, DXGI_FORMAT_R8G8B8A8_SNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB10_A2, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB10_A2UI, DXGI_FORMAT_R10G10B10A2_UINT, DXGI_FORMAT_R10G10B10A2_UINT, DXGI_FORMAT_R10G10B10A2_UINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_SRGB8, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_SRGB8_ALPHA8, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_R16F, DXGI_FORMAT_R16_FLOAT, DXGI_FORMAT_R16_FLOAT, DXGI_FORMAT_R16_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RG16F, DXGI_FORMAT_R16G16_FLOAT, DXGI_FORMAT_R16G16_FLOAT, DXGI_FORMAT_R16G16_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB16F, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGBA16F, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_R32F, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RG32F, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB32F, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGBA32F, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_R11F_G11F_B10F, DXGI_FORMAT_R11G11B10_FLOAT, DXGI_FORMAT_R11G11B10_FLOAT, DXGI_FORMAT_R11G11B10_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB9_E5, DXGI_FORMAT_R9G9B9E5_SHAREDEXP, DXGI_FORMAT_R9G9B9E5_SHAREDEXP, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_R8I, DXGI_FORMAT_R8_SINT, DXGI_FORMAT_R8_SINT, DXGI_FORMAT_R8_SINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_R8UI, DXGI_FORMAT_R8_UINT, DXGI_FORMAT_R8_UINT, DXGI_FORMAT_R8_UINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_R16I, DXGI_FORMAT_R16_SINT, DXGI_FORMAT_R16_SINT, DXGI_FORMAT_R16_SINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_R16UI, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_R32I, DXGI_FORMAT_R32_SINT, DXGI_FORMAT_R32_SINT, DXGI_FORMAT_R32_SINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_R32UI, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RG8I, DXGI_FORMAT_R8G8_SINT, DXGI_FORMAT_R8G8_SINT, DXGI_FORMAT_R8G8_SINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RG8UI, DXGI_FORMAT_R8G8_UINT, DXGI_FORMAT_R8G8_UINT, DXGI_FORMAT_R8G8_UINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RG16I, DXGI_FORMAT_R16G16_SINT, DXGI_FORMAT_R16G16_SINT, DXGI_FORMAT_R16G16_SINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RG16UI, DXGI_FORMAT_R16G16_UINT, DXGI_FORMAT_R16G16_UINT, DXGI_FORMAT_R16G16_UINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RG32I, DXGI_FORMAT_R32G32_SINT, DXGI_FORMAT_R32G32_SINT, DXGI_FORMAT_R32G32_SINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RG32UI, DXGI_FORMAT_R32G32_UINT, DXGI_FORMAT_R32G32_UINT, DXGI_FORMAT_R32G32_UINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB8I, DXGI_FORMAT_R8G8B8A8_SINT, DXGI_FORMAT_R8G8B8A8_SINT, DXGI_FORMAT_R8G8B8A8_SINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB8UI, DXGI_FORMAT_R8G8B8A8_UINT, DXGI_FORMAT_R8G8B8A8_UINT, DXGI_FORMAT_R8G8B8A8_UINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB16I, DXGI_FORMAT_R16G16B16A16_SINT, DXGI_FORMAT_R16G16B16A16_SINT, DXGI_FORMAT_R16G16B16A16_SINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB16UI, DXGI_FORMAT_R16G16B16A16_UINT, DXGI_FORMAT_R16G16B16A16_UINT, DXGI_FORMAT_R16G16B16A16_UINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB32I, DXGI_FORMAT_R32G32B32A32_SINT, DXGI_FORMAT_R32G32B32A32_SINT, DXGI_FORMAT_R32G32B32A32_SINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB32UI, DXGI_FORMAT_R32G32B32A32_UINT, DXGI_FORMAT_R32G32B32A32_UINT, DXGI_FORMAT_R32G32B32A32_UINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGBA8I, DXGI_FORMAT_R8G8B8A8_SINT, DXGI_FORMAT_R8G8B8A8_SINT, DXGI_FORMAT_R8G8B8A8_SINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGBA8UI, DXGI_FORMAT_R8G8B8A8_UINT, DXGI_FORMAT_R8G8B8A8_UINT, DXGI_FORMAT_R8G8B8A8_UINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGBA16I, DXGI_FORMAT_R16G16B16A16_SINT, DXGI_FORMAT_R16G16B16A16_SINT, DXGI_FORMAT_R16G16B16A16_SINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGBA16UI, DXGI_FORMAT_R16G16B16A16_UINT, DXGI_FORMAT_R16G16B16A16_UINT, DXGI_FORMAT_R16G16B16A16_UINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGBA32I, DXGI_FORMAT_R32G32B32A32_SINT, DXGI_FORMAT_R32G32B32A32_SINT, DXGI_FORMAT_R32G32B32A32_SINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGBA32UI, DXGI_FORMAT_R32G32B32A32_UINT, DXGI_FORMAT_R32G32B32A32_UINT, DXGI_FORMAT_R32G32B32A32_UINT, DXGI_FORMAT_UNKNOWN, AnyDevice); - - // Unsized formats, TODO: Are types of float and half float allowed for the unsized types? Would - // it change the DXGI format? - InsertD3D11FormatInfo(&map, GL_ALPHA, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_ALPHA, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, OnlyFL9_3); - InsertD3D11FormatInfo(&map, GL_LUMINANCE, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_LUMINANCE_ALPHA, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGB, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_RGBA, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_BGRA_EXT, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - - // From GL_EXT_texture_storage - // | GL internal format | D3D11 texture format | D3D11 SRV format | D3D11 RTV format | D3D11 DSV format | Requirements - InsertD3D11FormatInfo(&map, GL_ALPHA8_EXT, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_ALPHA8_EXT, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, OnlyFL9_3); - InsertD3D11FormatInfo(&map, GL_LUMINANCE8_EXT, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_ALPHA32F_EXT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_LUMINANCE32F_EXT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_ALPHA16F_EXT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_LUMINANCE16F_EXT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_LUMINANCE8_ALPHA8_EXT, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_LUMINANCE_ALPHA32F_EXT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_LUMINANCE_ALPHA16F_EXT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_BGRA8_EXT, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_BGRA4_ANGLEX, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_BGR5_A1_ANGLEX, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_UNKNOWN, AnyDevice); - - // Depth stencil formats - InsertD3D11FormatInfo(&map, GL_DEPTH_COMPONENT16, DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D16_UNORM, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_DEPTH_COMPONENT16, DXGI_FORMAT_D16_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D16_UNORM, OnlyFL9_3); - InsertD3D11FormatInfo(&map, GL_DEPTH_COMPONENT24, DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_R24_UNORM_X8_TYPELESS, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D24_UNORM_S8_UINT, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_DEPTH_COMPONENT24, DXGI_FORMAT_D24_UNORM_S8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D24_UNORM_S8_UINT, OnlyFL9_3); - InsertD3D11FormatInfo(&map, GL_DEPTH_COMPONENT32F, DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D32_FLOAT, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_DEPTH_COMPONENT32F, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL9_3); - InsertD3D11FormatInfo(&map, GL_DEPTH24_STENCIL8, DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_R24_UNORM_X8_TYPELESS, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D24_UNORM_S8_UINT, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_DEPTH24_STENCIL8, DXGI_FORMAT_D24_UNORM_S8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D24_UNORM_S8_UINT, OnlyFL9_3); - InsertD3D11FormatInfo(&map, GL_DEPTH32F_STENCIL8, DXGI_FORMAT_R32G8X24_TYPELESS, DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D32_FLOAT_S8X24_UINT, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_DEPTH32F_STENCIL8, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL9_3); - InsertD3D11FormatInfo(&map, GL_STENCIL_INDEX8, DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_X24_TYPELESS_G8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D24_UNORM_S8_UINT, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_STENCIL_INDEX8, DXGI_FORMAT_D24_UNORM_S8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D24_UNORM_S8_UINT, OnlyFL9_3); - - // From GL_ANGLE_depth_texture - // Since D3D11 doesn't have a D32_UNORM format, use D24S8 which has comparable precision and - // matches the ES3 format. - InsertD3D11FormatInfo(&map, GL_DEPTH_COMPONENT32_OES, DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_R24_UNORM_X8_TYPELESS, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D24_UNORM_S8_UINT, OnlyFL10Plus); - - // Compressed formats, From ES 3.0.1 spec, table 3.16 - // | GL internal format | D3D11 texture format | D3D11 SRV format | D3D11 RTV format | D3D11 DSV format | Requirements - InsertD3D11FormatInfo(&map, GL_COMPRESSED_R11_EAC, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_SIGNED_R11_EAC, DXGI_FORMAT_R8_SNORM, DXGI_FORMAT_R8_SNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_RG11_EAC, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_SIGNED_RG11_EAC, DXGI_FORMAT_R8G8_SNORM, DXGI_FORMAT_R8G8_SNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_RGB8_ETC2, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_SRGB8_ETC2, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_RGBA8_ETC2_EAC, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - - // From GL_ETC1_RGB8_OES - InsertD3D11FormatInfo(&map, GL_ETC1_RGB8_OES, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, AnyDevice); - - // From GL_EXT_texture_compression_dxt1 - InsertD3D11FormatInfo(&map, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, AnyDevice); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, AnyDevice); - - // From GL_ANGLE_texture_compression_dxt3 - InsertD3D11FormatInfo(&map, GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, AnyDevice); - - // From GL_ANGLE_texture_compression_dxt5 - InsertD3D11FormatInfo(&map, GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, AnyDevice); - // clang-format on - - return map; -} - -} // namespace - -TextureFormat::TextureFormat() - : texFormat(DXGI_FORMAT_UNKNOWN), - srvFormat(DXGI_FORMAT_UNKNOWN), - rtvFormat(DXGI_FORMAT_UNKNOWN), - dsvFormat(DXGI_FORMAT_UNKNOWN), - renderFormat(DXGI_FORMAT_UNKNOWN), - swizzleTexFormat(DXGI_FORMAT_UNKNOWN), - swizzleSRVFormat(DXGI_FORMAT_UNKNOWN), - swizzleRTVFormat(DXGI_FORMAT_UNKNOWN), - dataInitializerFunction(NULL), - loadFunctions() -{ -} - -const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, - const Renderer11DeviceCaps &renderer11DeviceCaps) -{ - static const D3D11ES3FormatMap &formatMap = BuildD3D11FormatMap(); - - D3D11ES3FormatMap::const_iterator iter = formatMap.find(internalFormat); - if (iter != formatMap.end()) - { - const std::vector &formatVector = iter->second; - for (size_t i = 0; i < formatVector.size(); i++) - { - const FormatSupportFunction supportFunction = formatVector[i].first; - const TextureFormat &textureFormat = formatVector[i].second; - - if (supportFunction(renderer11DeviceCaps)) - { - return textureFormat; - } - } - } - - static const TextureFormat defaultInfo; - return defaultInfo; -} - -} // namespace d3d11 - -} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.h index 9b275fdd29..1606a28a73 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.h @@ -22,6 +22,18 @@ namespace rx namespace d3d11 { +struct LoadImageFunctionInfo +{ + LoadImageFunctionInfo() : loadFunction(nullptr), requiresConversion(false) {} + LoadImageFunctionInfo(LoadImageFunction loadFunction, bool requiresConversion) + : loadFunction(loadFunction), requiresConversion(requiresConversion) + { + } + + LoadImageFunction loadFunction; + bool requiresConversion; +}; + struct TextureFormat { TextureFormat(); @@ -37,7 +49,7 @@ struct TextureFormat DXGI_FORMAT swizzleRTVFormat; InitializeTextureDataFunction dataInitializerFunction; - typedef std::map LoadFunctionMap; + typedef std::map LoadFunctionMap; LoadFunctionMap loadFunctions; }; @@ -49,4 +61,4 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalformat, } // namespace rx -#endif // LIBANGLE_RENDERER_D3D_D3D11_TEXTUREFORMATTABLE_H_ \ No newline at end of file +#endif // LIBANGLE_RENDERER_D3D_D3D11_TEXTUREFORMATTABLE_H_ diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table_autogen.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table_autogen.cpp index 8f96289c06..0b214c9756 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table_autogen.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table_autogen.cpp @@ -593,16 +593,7 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, } case GL_DEPTH24_STENCIL8: { - if (OnlyFL10Plus(renderer11DeviceCaps)) - { - static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, - DXGI_FORMAT_R24G8_TYPELESS, - DXGI_FORMAT_R24_UNORM_X8_TYPELESS, - DXGI_FORMAT_UNKNOWN, - DXGI_FORMAT_D24_UNORM_S8_UINT); - return textureFormat; - } - else if (OnlyFL9_3(renderer11DeviceCaps)) + if (OnlyFL9_3(renderer11DeviceCaps)) { static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, DXGI_FORMAT_D24_UNORM_S8_UINT, @@ -611,6 +602,15 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, DXGI_FORMAT_D24_UNORM_S8_UINT); return textureFormat; } + else if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R24G8_TYPELESS, + DXGI_FORMAT_R24_UNORM_X8_TYPELESS, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D24_UNORM_S8_UINT); + return textureFormat; + } else { break; @@ -618,16 +618,7 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, } case GL_DEPTH32F_STENCIL8: { - if (OnlyFL10Plus(renderer11DeviceCaps)) - { - static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, - DXGI_FORMAT_R32G8X24_TYPELESS, - DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, - DXGI_FORMAT_UNKNOWN, - DXGI_FORMAT_D32_FLOAT_S8X24_UINT); - return textureFormat; - } - else if (OnlyFL9_3(renderer11DeviceCaps)) + if (OnlyFL9_3(renderer11DeviceCaps)) { static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, DXGI_FORMAT_UNKNOWN, @@ -636,6 +627,15 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, DXGI_FORMAT_UNKNOWN); return textureFormat; } + else if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32G8X24_TYPELESS, + DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D32_FLOAT_S8X24_UINT); + return textureFormat; + } else { break; @@ -643,16 +643,7 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, } case GL_DEPTH_COMPONENT16: { - if (OnlyFL10Plus(renderer11DeviceCaps)) - { - static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, - DXGI_FORMAT_R16_TYPELESS, - DXGI_FORMAT_R16_UNORM, - DXGI_FORMAT_UNKNOWN, - DXGI_FORMAT_D16_UNORM); - return textureFormat; - } - else if (OnlyFL9_3(renderer11DeviceCaps)) + if (OnlyFL9_3(renderer11DeviceCaps)) { static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, DXGI_FORMAT_D16_UNORM, @@ -661,6 +652,15 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, DXGI_FORMAT_D16_UNORM); return textureFormat; } + else if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16_TYPELESS, + DXGI_FORMAT_R16_UNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D16_UNORM); + return textureFormat; + } else { break; @@ -668,16 +668,7 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, } case GL_DEPTH_COMPONENT24: { - if (OnlyFL10Plus(renderer11DeviceCaps)) - { - static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, - DXGI_FORMAT_R24G8_TYPELESS, - DXGI_FORMAT_R24_UNORM_X8_TYPELESS, - DXGI_FORMAT_UNKNOWN, - DXGI_FORMAT_D24_UNORM_S8_UINT); - return textureFormat; - } - else if (OnlyFL9_3(renderer11DeviceCaps)) + if (OnlyFL9_3(renderer11DeviceCaps)) { static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, DXGI_FORMAT_D24_UNORM_S8_UINT, @@ -686,6 +677,15 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, DXGI_FORMAT_D24_UNORM_S8_UINT); return textureFormat; } + else if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R24G8_TYPELESS, + DXGI_FORMAT_R24_UNORM_X8_TYPELESS, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D24_UNORM_S8_UINT); + return textureFormat; + } else { break; @@ -693,16 +693,7 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, } case GL_DEPTH_COMPONENT32F: { - if (OnlyFL10Plus(renderer11DeviceCaps)) - { - static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, - DXGI_FORMAT_R32_TYPELESS, - DXGI_FORMAT_R32_FLOAT, - DXGI_FORMAT_UNKNOWN, - DXGI_FORMAT_D32_FLOAT); - return textureFormat; - } - else if (OnlyFL9_3(renderer11DeviceCaps)) + if (OnlyFL9_3(renderer11DeviceCaps)) { static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, DXGI_FORMAT_UNKNOWN, @@ -711,6 +702,15 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, DXGI_FORMAT_UNKNOWN); return textureFormat; } + else if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32_TYPELESS, + DXGI_FORMAT_R32_FLOAT, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D32_FLOAT); + return textureFormat; + } else { break; @@ -732,6 +732,22 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, break; } } + case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_BC1_UNORM, + DXGI_FORMAT_BC1_UNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } case GL_ETC1_RGB8_OES: { if (AnyDevice(renderer11DeviceCaps)) @@ -1737,16 +1753,7 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, } case GL_STENCIL_INDEX8: { - if (OnlyFL10Plus(renderer11DeviceCaps)) - { - static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, - DXGI_FORMAT_R24G8_TYPELESS, - DXGI_FORMAT_X24_TYPELESS_G8_UINT, - DXGI_FORMAT_UNKNOWN, - DXGI_FORMAT_D24_UNORM_S8_UINT); - return textureFormat; - } - else if (OnlyFL9_3(renderer11DeviceCaps)) + if (OnlyFL9_3(renderer11DeviceCaps)) { static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, DXGI_FORMAT_D24_UNORM_S8_UINT, @@ -1755,6 +1762,15 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, DXGI_FORMAT_D24_UNORM_S8_UINT); return textureFormat; } + else if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R24G8_TYPELESS, + DXGI_FORMAT_X24_TYPELESS_G8_UINT, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D24_UNORM_S8_UINT); + return textureFormat; + } else { break; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_util.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_util.cpp deleted file mode 100644 index 6479f40849..0000000000 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_util.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// -// Copyright 2015 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. -// -// texture_format_util: -// Contains helper functions for texture_format_table -// - -#include "libANGLE/renderer/d3d/d3d11/texture_format_util.h" -#include "libANGLE/renderer/d3d/d3d11/formatutils11.h" -#include "libANGLE/renderer/d3d/loadimage.h" -#include "libANGLE/renderer/d3d/loadimage_etc.h" - -namespace rx -{ - -namespace d3d11 -{ - -namespace -{ - -// ES3 image loading functions vary based on: -// - the GL internal format (supplied to glTex*Image*D) -// - the GL data type given (supplied to glTex*Image*D) -// - the target DXGI_FORMAT that the image will be loaded into (which is chosen based on the D3D -// device's capabilities) -// This map type determines which loading function to use, based on these three parameters. -// Source formats and types are taken from Tables 3.2 and 3.3 of the ES 3 spec. -void UnimplementedLoadFunction(size_t width, - size_t height, - size_t depth, - const uint8_t *input, - size_t inputRowPitch, - size_t inputDepthPitch, - uint8_t *output, - size_t outputRowPitch, - size_t outputDepthPitch) -{ - UNIMPLEMENTED(); -} - -void UnreachableLoadFunction(size_t width, - size_t height, - size_t depth, - const uint8_t *input, - size_t inputRowPitch, - size_t inputDepthPitch, - uint8_t *output, - size_t outputRowPitch, - size_t outputDepthPitch) -{ - UNREACHABLE(); -} - -// A helper function to insert data into the D3D11LoadFunctionMap with fewer characters. -inline void InsertLoadFunction(D3D11LoadFunctionMap *map, GLenum internalFormat, GLenum type, - DXGI_FORMAT dxgiFormat, LoadImageFunction loadFunc) -{ - (*map)[internalFormat].push_back(GLTypeDXGIFunctionPair(type, DxgiFormatLoadFunctionPair(dxgiFormat, loadFunc))); -} - -} // namespace - -// TODO: This will be generated by a JSON file -const D3D11LoadFunctionMap &BuildD3D11LoadFunctionMap() -{ - static D3D11LoadFunctionMap map; - - // clang-format off - // | Internal format | Type | Target DXGI Format | Load function | - InsertLoadFunction(&map, GL_RGBA8, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadToNative ); - InsertLoadFunction(&map, GL_RGB5_A1, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadToNative ); - InsertLoadFunction(&map, GL_RGBA4, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadToNative ); - InsertLoadFunction(&map, GL_SRGB8_ALPHA8, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, LoadToNative ); - InsertLoadFunction(&map, GL_RGBA8_SNORM, GL_BYTE, DXGI_FORMAT_R8G8B8A8_SNORM, LoadToNative ); - InsertLoadFunction(&map, GL_RGBA4, GL_UNSIGNED_SHORT_4_4_4_4, DXGI_FORMAT_R8G8B8A8_UNORM, LoadRGBA4ToRGBA8 ); - InsertLoadFunction(&map, GL_RGBA4, GL_UNSIGNED_SHORT_4_4_4_4, DXGI_FORMAT_B4G4R4A4_UNORM, LoadRGBA4ToARGB4 ); - InsertLoadFunction(&map, GL_RGB10_A2, GL_UNSIGNED_INT_2_10_10_10_REV, DXGI_FORMAT_R10G10B10A2_UNORM, LoadToNative ); - InsertLoadFunction(&map, GL_RGB5_A1, GL_UNSIGNED_SHORT_5_5_5_1, DXGI_FORMAT_R8G8B8A8_UNORM, LoadRGB5A1ToRGBA8 ); - InsertLoadFunction(&map, GL_RGB5_A1, GL_UNSIGNED_SHORT_5_5_5_1, DXGI_FORMAT_B5G5R5A1_UNORM, LoadRGB5A1ToA1RGB5 ); - InsertLoadFunction(&map, GL_RGB5_A1, GL_UNSIGNED_INT_2_10_10_10_REV, DXGI_FORMAT_R8G8B8A8_UNORM, LoadRGB10A2ToRGBA8 ); - InsertLoadFunction(&map, GL_RGBA16F, GL_HALF_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, LoadToNative ); - InsertLoadFunction(&map, GL_RGBA16F, GL_HALF_FLOAT_OES, DXGI_FORMAT_R16G16B16A16_FLOAT, LoadToNative ); - InsertLoadFunction(&map, GL_RGBA32F, GL_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, LoadToNative ); - InsertLoadFunction(&map, GL_RGBA16F, GL_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, Load32FTo16F<4> ); - InsertLoadFunction(&map, GL_RGBA8UI, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UINT, LoadToNative ); - InsertLoadFunction(&map, GL_RGBA8I, GL_BYTE, DXGI_FORMAT_R8G8B8A8_SINT, LoadToNative ); - InsertLoadFunction(&map, GL_RGBA16UI, GL_UNSIGNED_SHORT, DXGI_FORMAT_R16G16B16A16_UINT, LoadToNative ); - InsertLoadFunction(&map, GL_RGBA16I, GL_SHORT, DXGI_FORMAT_R16G16B16A16_SINT, LoadToNative ); - InsertLoadFunction(&map, GL_RGBA32UI, GL_UNSIGNED_INT, DXGI_FORMAT_R32G32B32A32_UINT, LoadToNative ); - InsertLoadFunction(&map, GL_RGBA32I, GL_INT, DXGI_FORMAT_R32G32B32A32_SINT, LoadToNative ); - InsertLoadFunction(&map, GL_RGB10_A2UI, GL_UNSIGNED_INT_2_10_10_10_REV, DXGI_FORMAT_R10G10B10A2_UINT, LoadToNative ); - InsertLoadFunction(&map, GL_RGB8, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadToNative3To4 ); - InsertLoadFunction(&map, GL_RGB565, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadToNative3To4 ); - InsertLoadFunction(&map, GL_SRGB8, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, LoadToNative3To4 ); - InsertLoadFunction(&map, GL_RGB8_SNORM, GL_BYTE, DXGI_FORMAT_R8G8B8A8_SNORM, LoadToNative3To4 ); - InsertLoadFunction(&map, GL_RGB565, GL_UNSIGNED_SHORT_5_6_5, DXGI_FORMAT_R8G8B8A8_UNORM, LoadR5G6B5ToRGBA8 ); - InsertLoadFunction(&map, GL_RGB565, GL_UNSIGNED_SHORT_5_6_5, DXGI_FORMAT_B5G6R5_UNORM, LoadToNative ); - InsertLoadFunction(&map, GL_R11F_G11F_B10F, GL_UNSIGNED_INT_10F_11F_11F_REV, DXGI_FORMAT_R11G11B10_FLOAT, LoadToNative ); - InsertLoadFunction(&map, GL_RGB9_E5, GL_UNSIGNED_INT_5_9_9_9_REV, DXGI_FORMAT_R9G9B9E5_SHAREDEXP, LoadToNative ); - InsertLoadFunction(&map, GL_RGB16F, GL_HALF_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, LoadToNative3To4); - InsertLoadFunction(&map, GL_RGB16F, GL_HALF_FLOAT_OES, DXGI_FORMAT_R16G16B16A16_FLOAT, LoadToNative3To4); - InsertLoadFunction(&map, GL_R11F_G11F_B10F, GL_HALF_FLOAT, DXGI_FORMAT_R11G11B10_FLOAT, LoadRGB16FToRG11B10F ); - InsertLoadFunction(&map, GL_R11F_G11F_B10F, GL_HALF_FLOAT_OES, DXGI_FORMAT_R11G11B10_FLOAT, LoadRGB16FToRG11B10F ); - InsertLoadFunction(&map, GL_RGB9_E5, GL_HALF_FLOAT, DXGI_FORMAT_R9G9B9E5_SHAREDEXP, LoadRGB16FToRGB9E5 ); - InsertLoadFunction(&map, GL_RGB9_E5, GL_HALF_FLOAT_OES, DXGI_FORMAT_R9G9B9E5_SHAREDEXP, LoadRGB16FToRGB9E5 ); - InsertLoadFunction(&map, GL_RGB32F, GL_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, LoadToNative3To4); - InsertLoadFunction(&map, GL_RGB16F, GL_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, LoadRGB32FToRGBA16F ); - InsertLoadFunction(&map, GL_R11F_G11F_B10F, GL_FLOAT, DXGI_FORMAT_R11G11B10_FLOAT, LoadRGB32FToRG11B10F ); - InsertLoadFunction(&map, GL_RGB9_E5, GL_FLOAT, DXGI_FORMAT_R9G9B9E5_SHAREDEXP, LoadRGB32FToRGB9E5 ); - InsertLoadFunction(&map, GL_RGB8UI, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UINT, LoadToNative3To4 ); - InsertLoadFunction(&map, GL_RGB8I, GL_BYTE, DXGI_FORMAT_R8G8B8A8_SINT, LoadToNative3To4 ); - InsertLoadFunction(&map, GL_RGB16UI, GL_UNSIGNED_SHORT, DXGI_FORMAT_R16G16B16A16_UINT, LoadToNative3To4 ); - InsertLoadFunction(&map, GL_RGB16I, GL_SHORT, DXGI_FORMAT_R16G16B16A16_SINT, LoadToNative3To4 ); - InsertLoadFunction(&map, GL_RGB32UI, GL_UNSIGNED_INT, DXGI_FORMAT_R32G32B32A32_UINT, LoadToNative3To4 ); - InsertLoadFunction(&map, GL_RGB32I, GL_INT, DXGI_FORMAT_R32G32B32A32_SINT, LoadToNative3To4 ); - InsertLoadFunction(&map, GL_RG8, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8_UNORM, LoadToNative ); - InsertLoadFunction(&map, GL_RG8_SNORM, GL_BYTE, DXGI_FORMAT_R8G8_SNORM, LoadToNative ); - InsertLoadFunction(&map, GL_RG16F, GL_HALF_FLOAT, DXGI_FORMAT_R16G16_FLOAT, LoadToNative ); - InsertLoadFunction(&map, GL_RG16F, GL_HALF_FLOAT_OES, DXGI_FORMAT_R16G16_FLOAT, LoadToNative ); - InsertLoadFunction(&map, GL_RG32F, GL_FLOAT, DXGI_FORMAT_R32G32_FLOAT, LoadToNative ); - InsertLoadFunction(&map, GL_RG16F, GL_FLOAT, DXGI_FORMAT_R16G16_FLOAT, Load32FTo16F<2> ); - InsertLoadFunction(&map, GL_RG8UI, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8_UINT, LoadToNative ); - InsertLoadFunction(&map, GL_RG8I, GL_BYTE, DXGI_FORMAT_R8G8_SINT, LoadToNative ); - InsertLoadFunction(&map, GL_RG16UI, GL_UNSIGNED_SHORT, DXGI_FORMAT_R16G16_UINT, LoadToNative ); - InsertLoadFunction(&map, GL_RG16I, GL_SHORT, DXGI_FORMAT_R16G16_SINT, LoadToNative ); - InsertLoadFunction(&map, GL_RG32UI, GL_UNSIGNED_INT, DXGI_FORMAT_R32G32_UINT, LoadToNative ); - InsertLoadFunction(&map, GL_RG32I, GL_INT, DXGI_FORMAT_R32G32_SINT, LoadToNative ); - InsertLoadFunction(&map, GL_R8, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8_UNORM, LoadToNative ); - InsertLoadFunction(&map, GL_R8_SNORM, GL_BYTE, DXGI_FORMAT_R8_SNORM, LoadToNative ); - InsertLoadFunction(&map, GL_R16F, GL_HALF_FLOAT, DXGI_FORMAT_R16_FLOAT, LoadToNative ); - InsertLoadFunction(&map, GL_R16F, GL_HALF_FLOAT_OES, DXGI_FORMAT_R16_FLOAT, LoadToNative ); - InsertLoadFunction(&map, GL_R32F, GL_FLOAT, DXGI_FORMAT_R32_FLOAT, LoadToNative ); - InsertLoadFunction(&map, GL_R16F, GL_FLOAT, DXGI_FORMAT_R16_FLOAT, Load32FTo16F<1> ); - InsertLoadFunction(&map, GL_R8UI, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8_UINT, LoadToNative ); - InsertLoadFunction(&map, GL_R8I, GL_BYTE, DXGI_FORMAT_R8_SINT, LoadToNative ); - InsertLoadFunction(&map, GL_R16UI, GL_UNSIGNED_SHORT, DXGI_FORMAT_R16_UINT, LoadToNative ); - InsertLoadFunction(&map, GL_R16I, GL_SHORT, DXGI_FORMAT_R16_SINT, LoadToNative ); - InsertLoadFunction(&map, GL_R32UI, GL_UNSIGNED_INT, DXGI_FORMAT_R32_UINT, LoadToNative ); - InsertLoadFunction(&map, GL_R32I, GL_INT, DXGI_FORMAT_R32_SINT, LoadToNative ); - - InsertLoadFunction(&map, GL_DEPTH_COMPONENT16, GL_UNSIGNED_SHORT, DXGI_FORMAT_R16_TYPELESS, LoadToNative ); - InsertLoadFunction(&map, GL_DEPTH_COMPONENT16, GL_UNSIGNED_SHORT, DXGI_FORMAT_D16_UNORM, LoadToNative ); - InsertLoadFunction(&map, GL_DEPTH_COMPONENT24, GL_UNSIGNED_INT, DXGI_FORMAT_R24G8_TYPELESS, LoadR32ToR24G8 ); - InsertLoadFunction(&map, GL_DEPTH_COMPONENT24, GL_UNSIGNED_INT, DXGI_FORMAT_D24_UNORM_S8_UINT, LoadR32ToR24G8 ); - InsertLoadFunction(&map, GL_DEPTH_COMPONENT16, GL_UNSIGNED_INT, DXGI_FORMAT_R16_TYPELESS, LoadR32ToR16 ); - InsertLoadFunction(&map, GL_DEPTH_COMPONENT32F, GL_FLOAT, DXGI_FORMAT_R32_TYPELESS, LoadToNative ); - InsertLoadFunction(&map, GL_DEPTH_COMPONENT32F, GL_FLOAT, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); - InsertLoadFunction(&map, GL_DEPTH24_STENCIL8, GL_UNSIGNED_INT_24_8, DXGI_FORMAT_R24G8_TYPELESS, LoadR32ToR24G8 ); - InsertLoadFunction(&map, GL_DEPTH24_STENCIL8, GL_UNSIGNED_INT_24_8, DXGI_FORMAT_D24_UNORM_S8_UINT, LoadR32ToR24G8 ); - InsertLoadFunction(&map, GL_DEPTH32F_STENCIL8, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, DXGI_FORMAT_R32G8X24_TYPELESS, LoadToNative ); - InsertLoadFunction(&map, GL_DEPTH32F_STENCIL8, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); - InsertLoadFunction(&map, GL_STENCIL_INDEX8, DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); - InsertLoadFunction(&map, GL_STENCIL_INDEX8, DXGI_FORMAT_D24_UNORM_S8_UINT, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); - - // Unsized formats - // Load functions are unreachable because they are converted to sized internal formats based on - // the format and type before loading takes place. - InsertLoadFunction(&map, GL_RGBA, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnreachableLoadFunction ); - InsertLoadFunction(&map, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, DXGI_FORMAT_UNKNOWN, UnreachableLoadFunction ); - InsertLoadFunction(&map, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, DXGI_FORMAT_UNKNOWN, UnreachableLoadFunction ); - InsertLoadFunction(&map, GL_RGB, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnreachableLoadFunction ); - InsertLoadFunction(&map, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, DXGI_FORMAT_UNKNOWN, UnreachableLoadFunction ); - InsertLoadFunction(&map, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnreachableLoadFunction ); - InsertLoadFunction(&map, GL_LUMINANCE, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnreachableLoadFunction ); - InsertLoadFunction(&map, GL_ALPHA, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnreachableLoadFunction ); - InsertLoadFunction(&map, GL_BGRA_EXT, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnreachableLoadFunction ); - - // From GL_OES_texture_float - InsertLoadFunction(&map, GL_LUMINANCE_ALPHA, GL_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, LoadLA32FToRGBA32F ); - InsertLoadFunction(&map, GL_LUMINANCE, GL_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, LoadL32FToRGBA32F ); - InsertLoadFunction(&map, GL_ALPHA, GL_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, LoadA32FToRGBA32F ); - - // From GL_OES_texture_half_float - InsertLoadFunction(&map, GL_LUMINANCE_ALPHA, GL_HALF_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, LoadLA16FToRGBA16F ); - InsertLoadFunction(&map, GL_LUMINANCE_ALPHA, GL_HALF_FLOAT_OES, DXGI_FORMAT_R16G16B16A16_FLOAT, LoadLA16FToRGBA16F ); - InsertLoadFunction(&map, GL_LUMINANCE, GL_HALF_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, LoadL16FToRGBA16F ); - InsertLoadFunction(&map, GL_LUMINANCE, GL_HALF_FLOAT_OES, DXGI_FORMAT_R16G16B16A16_FLOAT, LoadL16FToRGBA16F ); - InsertLoadFunction(&map, GL_ALPHA, GL_HALF_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, LoadA16FToRGBA16F ); - InsertLoadFunction(&map, GL_ALPHA, GL_HALF_FLOAT_OES, DXGI_FORMAT_R16G16B16A16_FLOAT, LoadA16FToRGBA16F ); - - // From GL_EXT_texture_storage - InsertLoadFunction(&map, GL_ALPHA8_EXT, GL_UNSIGNED_BYTE, DXGI_FORMAT_A8_UNORM, LoadToNative ); - InsertLoadFunction(&map, GL_ALPHA8_EXT, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadA8ToRGBA8 ); - InsertLoadFunction(&map, GL_LUMINANCE8_EXT, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, LoadL8ToRGBA8 ); - InsertLoadFunction(&map, GL_LUMINANCE8_ALPHA8_EXT, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, LoadLA8ToRGBA8 ); - InsertLoadFunction(&map, GL_ALPHA32F_EXT, GL_FLOAT, DXGI_FORMAT_UNKNOWN, LoadA32FToRGBA32F ); - InsertLoadFunction(&map, GL_LUMINANCE32F_EXT, GL_FLOAT, DXGI_FORMAT_UNKNOWN, LoadL32FToRGBA32F ); - InsertLoadFunction(&map, GL_LUMINANCE_ALPHA32F_EXT, GL_FLOAT, DXGI_FORMAT_UNKNOWN, LoadLA32FToRGBA32F ); - InsertLoadFunction(&map, GL_ALPHA16F_EXT, GL_HALF_FLOAT, DXGI_FORMAT_UNKNOWN, LoadA16FToRGBA16F ); - InsertLoadFunction(&map, GL_ALPHA16F_EXT, GL_HALF_FLOAT_OES, DXGI_FORMAT_UNKNOWN, LoadA16FToRGBA16F ); - InsertLoadFunction(&map, GL_LUMINANCE16F_EXT, GL_HALF_FLOAT, DXGI_FORMAT_UNKNOWN, LoadL16FToRGBA16F ); - InsertLoadFunction(&map, GL_LUMINANCE16F_EXT, GL_HALF_FLOAT_OES, DXGI_FORMAT_UNKNOWN, LoadL16FToRGBA16F ); - InsertLoadFunction(&map, GL_LUMINANCE_ALPHA16F_EXT, GL_HALF_FLOAT, DXGI_FORMAT_UNKNOWN, LoadLA16FToRGBA16F ); - InsertLoadFunction(&map, GL_LUMINANCE_ALPHA16F_EXT, GL_HALF_FLOAT_OES, DXGI_FORMAT_UNKNOWN, LoadLA16FToRGBA16F ); - - // From GL_ANGLE_depth_texture - InsertLoadFunction(&map, GL_DEPTH_COMPONENT32_OES, GL_UNSIGNED_INT, DXGI_FORMAT_UNKNOWN, LoadR32ToR24G8 ); - - // From GL_EXT_texture_format_BGRA8888 - InsertLoadFunction(&map, GL_BGRA8_EXT, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, LoadToNative ); - InsertLoadFunction(&map, GL_BGRA4_ANGLEX, GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT, DXGI_FORMAT_UNKNOWN, LoadRGBA4ToRGBA8 ); - InsertLoadFunction(&map, GL_BGRA4_ANGLEX, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, LoadToNative ); - InsertLoadFunction(&map, GL_BGR5_A1_ANGLEX, GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT, DXGI_FORMAT_UNKNOWN, LoadRGB5A1ToRGBA8 ); - InsertLoadFunction(&map, GL_BGR5_A1_ANGLEX, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, LoadToNative ); - // Compressed formats - // From ES 3.0.1 spec, table 3.16 - // | Internal format | Type | Target DXGI Format | Load function - InsertLoadFunction(&map, GL_COMPRESSED_R11_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8_UNORM, LoadEACR11ToR8 ); - InsertLoadFunction(&map, GL_COMPRESSED_SIGNED_R11_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8_SNORM, LoadEACR11SToR8 ); - InsertLoadFunction(&map, GL_COMPRESSED_RG11_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8_UNORM, LoadEACRG11ToRG8 ); - InsertLoadFunction(&map, GL_COMPRESSED_SIGNED_RG11_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8_SNORM, LoadEACRG11SToRG8 ); - InsertLoadFunction(&map, GL_COMPRESSED_RGB8_ETC2, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadETC2RGB8ToRGBA8 ); - InsertLoadFunction(&map, GL_COMPRESSED_SRGB8_ETC2, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, LoadETC2SRGB8ToRGBA8 ); - InsertLoadFunction(&map, GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadETC2RGB8A1ToRGBA8 ); - InsertLoadFunction(&map, GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, LoadETC2SRGB8A1ToRGBA8); - InsertLoadFunction(&map, GL_COMPRESSED_RGBA8_ETC2_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadETC2RGBA8ToRGBA8 ); - InsertLoadFunction(&map, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, LoadETC2SRGBA8ToSRGBA8); - - // From GL_ETC1_RGB8_OES - InsertLoadFunction(&map, GL_ETC1_RGB8_OES, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadETC1RGB8ToRGBA8 ); - - // From GL_EXT_texture_compression_dxt1 - InsertLoadFunction(&map, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, LoadCompressedToNative<4, 4, 8> ); - InsertLoadFunction(&map, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, LoadCompressedToNative<4, 4, 8> ); - - // From GL_ANGLE_texture_compression_dxt3 - InsertLoadFunction(&map, GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, LoadCompressedToNative<4, 4, 16> ); - - // From GL_ANGLE_texture_compression_dxt5 - InsertLoadFunction(&map, GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, LoadCompressedToNative<4, 4, 16> ); - // clang-format on - - return map; -} - -typedef std::pair - InternalFormatInitializerPair; - -// TODO: This should be generated by a JSON file -const InternalFormatInitializerMap &BuildInternalFormatInitializerMap() -{ - static InternalFormatInitializerMap map; - - map.insert(InternalFormatInitializerPair( - InitializeTextureFormatPair(GL_RGB8, DXGI_FORMAT_R8G8B8A8_UNORM), - Initialize4ComponentData)); - map.insert(InternalFormatInitializerPair( - InitializeTextureFormatPair(GL_RGB565, DXGI_FORMAT_R8G8B8A8_UNORM), - Initialize4ComponentData)); - map.insert(InternalFormatInitializerPair( - InitializeTextureFormatPair(GL_SRGB8, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB), - Initialize4ComponentData)); - map.insert(InternalFormatInitializerPair( - InitializeTextureFormatPair(GL_RGB16F, DXGI_FORMAT_R16G16B16A16_FLOAT), - Initialize4ComponentData)); - map.insert(InternalFormatInitializerPair( - InitializeTextureFormatPair(GL_RGB32F, DXGI_FORMAT_R32G32B32A32_FLOAT), - Initialize4ComponentData)); - map.insert(InternalFormatInitializerPair( - InitializeTextureFormatPair(GL_RGB8UI, DXGI_FORMAT_R8G8B8A8_UINT), - Initialize4ComponentData)); - map.insert(InternalFormatInitializerPair( - InitializeTextureFormatPair(GL_RGB8I, DXGI_FORMAT_R8G8B8A8_SINT), - Initialize4ComponentData)); - map.insert(InternalFormatInitializerPair( - InitializeTextureFormatPair(GL_RGB16UI, DXGI_FORMAT_R16G16B16A16_UINT), - Initialize4ComponentData)); - map.insert(InternalFormatInitializerPair( - InitializeTextureFormatPair(GL_RGB16I, DXGI_FORMAT_R16G16B16A16_SINT), - Initialize4ComponentData)); - map.insert(InternalFormatInitializerPair( - InitializeTextureFormatPair(GL_RGB32UI, DXGI_FORMAT_R32G32B32A32_UINT), - Initialize4ComponentData)); - map.insert(InternalFormatInitializerPair( - InitializeTextureFormatPair(GL_RGB32I, DXGI_FORMAT_R32G32B32A32_SINT), - Initialize4ComponentData)); - - return map; -} - -} // namespace d3d11 - -} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow.cpp index c0f633127f..0efb1a6f5f 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow.cpp @@ -11,14 +11,46 @@ #include "common/debug.h" +#include + +#ifdef HAS_DIRECT_COMPOSITION +#include +#endif // HAS_DIRECT_COMPOSITION + namespace rx { -NativeWindow::NativeWindow(EGLNativeWindowType window, const egl::Config *) - : mWindow(window) +NativeWindow::NativeWindow(EGLNativeWindowType window, + const egl::Config *config, + bool directComposition) + : mWindow(window), + mDirectComposition(directComposition), + +#ifdef HAS_DIRECT_COMPOSITION + + mDevice(nullptr), + mCompositionTarget(nullptr), + mVisual(nullptr), + +#endif // HAS_DIRECT_COMPOSITION + + mConfig(config) { } +NativeWindow::~NativeWindow() +{ + +#ifdef HAS_DIRECT_COMPOSITION + + SafeRelease(mCompositionTarget); + SafeRelease(mDevice); + SafeRelease(mVisual); + +#endif // HAS_DIRECT_COMPOSITION + +} + bool NativeWindow::initialize() { return true; @@ -49,6 +81,88 @@ HRESULT NativeWindow::createSwapChain(ID3D11Device* device, DXGIFactory* factory return E_INVALIDARG; } + if (mDirectComposition) + { +#ifdef HAS_DIRECT_COMPOSITION + HMODULE dcomp = ::GetModuleHandle(TEXT("dcomp.dll")); + if (!dcomp) + { + return E_INVALIDARG; + } + + typedef HRESULT(WINAPI * PFN_DCOMPOSITION_CREATE_DEVICE)( + IDXGIDevice * dxgiDevice, REFIID iid, void **dcompositionDevice); + PFN_DCOMPOSITION_CREATE_DEVICE createDComp = + reinterpret_cast( + GetProcAddress(dcomp, "DCompositionCreateDevice")); + if (!createDComp) + { + return E_INVALIDARG; + } + + if (!mDevice) + { + IDXGIDevice *dxgiDevice = d3d11::DynamicCastComObject(device); + HRESULT result = createDComp(dxgiDevice, __uuidof(IDCompositionDevice), + reinterpret_cast(&mDevice)); + SafeRelease(dxgiDevice); + + if (FAILED(result)) + { + return result; + } + } + + if (!mCompositionTarget) + { + HRESULT result = mDevice->CreateTargetForHwnd(mWindow, TRUE, &mCompositionTarget); + if (FAILED(result)) + { + return result; + } + } + + if (!mVisual) + { + HRESULT result = mDevice->CreateVisual(&mVisual); + if (FAILED(result)) + { + return result; + } + } + + IDXGIFactory2 *factory2 = d3d11::DynamicCastComObject(factory); + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0}; + swapChainDesc.Width = width; + swapChainDesc.Height = height; + swapChainDesc.Format = format; + swapChainDesc.Stereo = FALSE; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.BufferUsage = + DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_SHADER_INPUT; + swapChainDesc.BufferCount = 2; + swapChainDesc.Scaling = DXGI_SCALING_STRETCH; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + swapChainDesc.AlphaMode = + mConfig->alphaSize == 0 ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; + swapChainDesc.Flags = 0; + IDXGISwapChain1 *swapChain1 = nullptr; + HRESULT result = + factory2->CreateSwapChainForComposition(device, &swapChainDesc, nullptr, &swapChain1); + if (SUCCEEDED(result)) + { + *swapChain = static_cast(swapChain1); + } + mVisual->SetContent(swapChain1); + mCompositionTarget->SetRoot(mVisual); + SafeRelease(factory2); + return result; +#else // HAS_DIRECT_COMPOSITION + return E_INVALIDARG; +#endif // HAS_DIRECT_COMPOSITION + } + // Use IDXGIFactory2::CreateSwapChainForHwnd if DXGI 1.2 is available to create a DXGI_SWAP_EFFECT_SEQUENTIAL swap chain. IDXGIFactory2 *factory2 = d3d11::DynamicCastComObject(factory); if (factory2 != nullptr) @@ -60,7 +174,8 @@ HRESULT NativeWindow::createSwapChain(ID3D11Device* device, DXGIFactory* factory swapChainDesc.Stereo = FALSE; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER; + swapChainDesc.BufferUsage = + DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_BACK_BUFFER; swapChainDesc.BufferCount = 1; swapChainDesc.Scaling = DXGI_SCALING_STRETCH; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL; @@ -85,7 +200,8 @@ HRESULT NativeWindow::createSwapChain(ID3D11Device* device, DXGIFactory* factory swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; swapChainDesc.BufferDesc.RefreshRate.Numerator = 0; swapChainDesc.BufferDesc.RefreshRate.Denominator = 1; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER; + swapChainDesc.BufferUsage = + DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_BACK_BUFFER; swapChainDesc.Flags = 0; swapChainDesc.OutputWindow = mWindow; swapChainDesc.SampleDesc.Count = 1; @@ -95,5 +211,20 @@ HRESULT NativeWindow::createSwapChain(ID3D11Device* device, DXGIFactory* factory return factory->CreateSwapChain(device, &swapChainDesc, swapChain); } -#endif +#endif // ANGLE_ENABLE_D3D11 + +void NativeWindow::commitChange() +{ + +#ifdef HAS_DIRECT_COMPOSITION + + if (mDevice) + { + mDevice->Commit(); + } + +#endif // HAS_DIRECT_COMPOSITION + } + +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp index 1ba736e7cc..71f0e4208d 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp @@ -154,7 +154,8 @@ HRESULT CoreWindowNativeWindow::createSwapChain(ID3D11Device *device, swapChainDesc.Stereo = FALSE; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER; + swapChainDesc.BufferUsage = + DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER; swapChainDesc.BufferCount = 2; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; swapChainDesc.Scaling = DXGI_SCALING_STRETCH; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.cpp index 5eb1d8c440..47a6daed2d 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.cpp @@ -11,12 +11,22 @@ namespace rx { -NativeWindow::NativeWindow(EGLNativeWindowType window, const egl::Config *config) +NativeWindow::NativeWindow(EGLNativeWindowType window, + const egl::Config *config, + bool directComposition) { mWindow = window; mConfig = config; } +NativeWindow::~NativeWindow() +{ +} + +void NativeWindow::commitChange() +{ +} + bool NativeWindow::initialize() { // If the native window type is a IPropertySet, extract the diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.cpp index 693b76888c..1ed3645b9c 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.cpp @@ -257,7 +257,8 @@ HRESULT SwapChainPanelNativeWindow::createSwapChain(ID3D11Device *device, swapChainDesc.Stereo = FALSE; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER; + swapChainDesc.BufferUsage = + DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER; swapChainDesc.BufferCount = 2; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; swapChainDesc.Scaling = DXGI_SCALING_STRETCH; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Framebuffer9.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Framebuffer9.cpp index 15e3167433..9c269a8565 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Framebuffer9.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Framebuffer9.cpp @@ -22,8 +22,7 @@ namespace rx { Framebuffer9::Framebuffer9(const gl::Framebuffer::Data &data, Renderer9 *renderer) - : FramebufferD3D(data), - mRenderer(renderer) + : FramebufferD3D(data, renderer), mRenderer(renderer) { ASSERT(mRenderer != nullptr); } @@ -53,7 +52,7 @@ gl::Error Framebuffer9::invalidateSub(size_t, const GLenum *, const gl::Rectangl return gl::Error(GL_NO_ERROR); } -gl::Error Framebuffer9::clear(const gl::State &state, const ClearParameters &clearParams) +gl::Error Framebuffer9::clear(const gl::Data &data, const ClearParameters &clearParams) { const gl::FramebufferAttachment *colorAttachment = mData.getColorAttachment(0); const gl::FramebufferAttachment *depthStencilAttachment = mData.getDepthOrStencilAttachment(); @@ -64,11 +63,12 @@ gl::Error Framebuffer9::clear(const gl::State &state, const ClearParameters &cle return error; } - float nearZ = state.getNearPlane(); - float farZ = state.getFarPlane(); - mRenderer->setViewport(state.getViewport(), nearZ, farZ, GL_TRIANGLES, state.getRasterizerState().frontFace, true); + float nearZ = data.state->getNearPlane(); + float farZ = data.state->getFarPlane(); + mRenderer->setViewport(data.caps, data.state->getViewport(), nearZ, farZ, GL_TRIANGLES, + data.state->getRasterizerState().frontFace, true); - mRenderer->setScissorRectangle(state.getScissor(), state.isScissorTestEnabled()); + mRenderer->setScissorRectangle(data.state->getScissor(), data.state->isScissorTestEnabled()); return mRenderer->clear(clearParams, colorAttachment, depthStencilAttachment); } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Framebuffer9.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Framebuffer9.h index 88055d3e4f..fe12079ae0 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Framebuffer9.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Framebuffer9.h @@ -26,7 +26,7 @@ class Framebuffer9 : public FramebufferD3D gl::Error invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area) override; private: - gl::Error clear(const gl::State &state, const ClearParameters &clearParams) override; + gl::Error clear(const gl::Data &data, const ClearParameters &clearParams) override; gl::Error readPixelsImpl(const gl::Rectangle &area, GLenum format, diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Image9.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Image9.cpp index df0fae4193..fec7e3e19d 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Image9.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Image9.cpp @@ -549,7 +549,9 @@ gl::Error Image9::loadCompressedData(const gl::Box &area, const void *input) } // This implements glCopyTex[Sub]Image2D for non-renderable internal texture formats and incomplete textures -gl::Error Image9::copy(const gl::Offset &destOffset, const gl::Rectangle &sourceArea, RenderTargetD3D *source) +gl::Error Image9::copyFromRTInternal(const gl::Offset &destOffset, + const gl::Rectangle &sourceArea, + RenderTargetD3D *source) { ASSERT(source); @@ -777,11 +779,35 @@ gl::Error Image9::copy(const gl::Offset &destOffset, const gl::Rectangle &source return gl::Error(GL_NO_ERROR); } -gl::Error Image9::copy(const gl::Offset &destOffset, const gl::Box &area, const gl::ImageIndex &srcIndex, TextureStorage *srcStorage) +gl::Error Image9::copyFromTexStorage(const gl::ImageIndex &imageIndex, TextureStorage *source) { - // Currently unreachable, due to only being used in a D3D11-only workaround - UNIMPLEMENTED(); - return gl::Error(GL_INVALID_OPERATION); + RenderTargetD3D *renderTarget = nullptr; + gl::Error error = source->getRenderTarget(imageIndex, &renderTarget); + if (error.isError()) + { + return error; + } + + gl::Rectangle sourceArea(0, 0, mWidth, mHeight); + return copyFromRTInternal(gl::Offset(), sourceArea, renderTarget); } +gl::Error Image9::copyFromFramebuffer(const gl::Offset &destOffset, + const gl::Rectangle &sourceArea, + const gl::Framebuffer *source) +{ + const gl::FramebufferAttachment *srcAttachment = source->getReadColorbuffer(); + ASSERT(srcAttachment); + + RenderTargetD3D *renderTarget = NULL; + gl::Error error = srcAttachment->getRenderTarget(&renderTarget); + if (error.isError()) + { + return error; + } + + ASSERT(renderTarget); + return copyFromRTInternal(destOffset, sourceArea, renderTarget); } + +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Image9.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Image9.h index 0bc6d7653f..91448cc849 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Image9.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Image9.h @@ -45,9 +45,10 @@ class Image9 : public ImageD3D virtual gl::Error loadData(const gl::Box &area, const gl::PixelUnpackState &unpack, GLenum type, const void *input); virtual gl::Error loadCompressedData(const gl::Box &area, const void *input); - virtual gl::Error copy(const gl::Offset &destOffset, const gl::Rectangle &sourceArea, RenderTargetD3D *source); - virtual gl::Error copy(const gl::Offset &destOffset, const gl::Box &sourceArea, - const gl::ImageIndex &sourceIndex, TextureStorage *source); + gl::Error copyFromTexStorage(const gl::ImageIndex &imageIndex, TextureStorage *source) override; + gl::Error copyFromFramebuffer(const gl::Offset &destOffset, + const gl::Rectangle &sourceArea, + const gl::Framebuffer *source) override; private: gl::Error getSurface(IDirect3DSurface9 **outSurface); @@ -59,6 +60,10 @@ class Image9 : public ImageD3D gl::Error lock(D3DLOCKED_RECT *lockedRect, const RECT &rect); void unlock(); + gl::Error copyFromRTInternal(const gl::Offset &destOffset, + const gl::Rectangle &sourceArea, + RenderTargetD3D *source); + Renderer9 *mRenderer; D3DPOOL mD3DPool; // can only be D3DPOOL_SYSTEMMEM or D3DPOOL_MANAGED since it needs to be lockable. diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Query9.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Query9.cpp index 96f12d7868..c826abf81c 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Query9.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Query9.cpp @@ -66,7 +66,14 @@ gl::Error Query9::end() return gl::Error(GL_NO_ERROR); } -gl::Error Query9::getResult(GLuint *params) +gl::Error Query9::queryCounter() +{ + UNIMPLEMENTED(); + return gl::Error(GL_INVALID_OPERATION, "Unimplemented"); +} + +template +gl::Error Query9::getResultBase(T *params) { while (!mQueryFinished) { @@ -83,12 +90,31 @@ gl::Error Query9::getResult(GLuint *params) } ASSERT(mQueryFinished); - *params = mResult; - + *params = static_cast(mResult); return gl::Error(GL_NO_ERROR); } -gl::Error Query9::isResultAvailable(GLuint *available) +gl::Error Query9::getResult(GLint *params) +{ + return getResultBase(params); +} + +gl::Error Query9::getResult(GLuint *params) +{ + return getResultBase(params); +} + +gl::Error Query9::getResult(GLint64 *params) +{ + return getResultBase(params); +} + +gl::Error Query9::getResult(GLuint64 *params) +{ + return getResultBase(params); +} + +gl::Error Query9::isResultAvailable(bool *available) { gl::Error error = testQuery(); if (error.isError()) @@ -96,7 +122,7 @@ gl::Error Query9::isResultAvailable(GLuint *available) return error; } - *available = (mQueryFinished ? GL_TRUE : GL_FALSE); + *available = mQueryFinished; return gl::Error(GL_NO_ERROR); } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Query9.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Query9.h index 399da2ed83..9d17711a00 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Query9.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Query9.h @@ -23,13 +23,20 @@ class Query9 : public QueryImpl virtual gl::Error begin(); virtual gl::Error end(); + virtual gl::Error queryCounter(); + virtual gl::Error getResult(GLint *params); virtual gl::Error getResult(GLuint *params); - virtual gl::Error isResultAvailable(GLuint *available); + virtual gl::Error getResult(GLint64 *params); + virtual gl::Error getResult(GLuint64 *params); + virtual gl::Error isResultAvailable(bool *available); private: gl::Error testQuery(); - GLuint mResult; + template + gl::Error getResultBase(T *params); + + GLuint64 mResult; bool mQueryFinished; Renderer9 *mRenderer; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp index d6d0b78195..145c346e8e 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp @@ -36,6 +36,7 @@ #include "libANGLE/renderer/d3d/d3d9/TextureStorage9.h" #include "libANGLE/renderer/d3d/d3d9/VertexArray9.h" #include "libANGLE/renderer/d3d/d3d9/VertexBuffer9.h" +#include "libANGLE/renderer/d3d/CompilerD3D.h" #include "libANGLE/renderer/d3d/DeviceD3D.h" #include "libANGLE/renderer/d3d/FramebufferD3D.h" #include "libANGLE/renderer/d3d/IndexDataManager.h" @@ -76,8 +77,7 @@ enum MAX_TEXTURE_IMAGE_UNITS_VTF_SM3 = 4 }; -Renderer9::Renderer9(egl::Display *display) - : RendererD3D(display) +Renderer9::Renderer9(egl::Display *display) : RendererD3D(display), mStateManager(this) { mD3d9Module = NULL; @@ -132,6 +132,8 @@ Renderer9::Renderer9(egl::Display *display) mAppliedProgramSerial = 0; initializeDebugAnnotator(); + + mEGLDevice = nullptr; } Renderer9::~Renderer9() @@ -154,6 +156,7 @@ void Renderer9::release() releaseDeviceResources(); + SafeDelete(mEGLDevice); SafeRelease(mDevice); SafeRelease(mDeviceEx); SafeRelease(mD3d9); @@ -522,11 +525,14 @@ void Renderer9::generateDisplayExtensions(egl::DisplayExtensions *outExtensions) outExtensions->postSubBuffer = true; outExtensions->createContext = true; outExtensions->deviceQuery = true; + outExtensions->createContextNoError = true; outExtensions->image = true; outExtensions->imageBase = true; outExtensions->glTexture2DImage = true; outExtensions->glRenderbufferImage = true; + + outExtensions->flexibleSurfaceCompatibility = true; } void Renderer9::startScene() @@ -648,9 +654,19 @@ gl::Error Renderer9::finish() return gl::Error(GL_NO_ERROR); } -SwapChainD3D *Renderer9::createSwapChain(NativeWindow nativeWindow, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat) +SwapChainD3D *Renderer9::createSwapChain(NativeWindow nativeWindow, + HANDLE shareHandle, + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation) { - return new SwapChain9(this, nativeWindow, shareHandle, backBufferFormat, depthBufferFormat); + return new SwapChain9(this, nativeWindow, shareHandle, backBufferFormat, depthBufferFormat, + orientation); +} + +CompilerImpl *Renderer9::createCompiler() +{ + return new CompilerD3D(SH_HLSL_3_0_OUTPUT); } void *Renderer9::getD3DDevice() @@ -883,353 +899,77 @@ gl::Error Renderer9::setUniformBuffers(const gl::Data &/*data*/, return gl::Error(GL_NO_ERROR); } -gl::Error Renderer9::setRasterizerState(const gl::RasterizerState &rasterState) +void Renderer9::syncState(const gl::State &state, const gl::State::DirtyBits &bitmask) { - bool rasterStateChanged = mForceSetRasterState || memcmp(&rasterState, &mCurRasterState, sizeof(gl::RasterizerState)) != 0; - - if (rasterStateChanged) - { - // Set the cull mode - if (rasterState.cullFace) - { - mDevice->SetRenderState(D3DRS_CULLMODE, gl_d3d9::ConvertCullMode(rasterState.cullMode, rasterState.frontFace)); - } - else - { - mDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - } - - if (rasterState.polygonOffsetFill) - { - if (mCurDepthSize > 0) - { - mDevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, *(DWORD*)&rasterState.polygonOffsetFactor); - - float depthBias = ldexp(rasterState.polygonOffsetUnits, -static_cast(mCurDepthSize)); - mDevice->SetRenderState(D3DRS_DEPTHBIAS, *(DWORD*)&depthBias); - } - } - else - { - mDevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, 0); - mDevice->SetRenderState(D3DRS_DEPTHBIAS, 0); - } - - mCurRasterState = rasterState; - } - - mForceSetRasterState = false; - - return gl::Error(GL_NO_ERROR); + mStateManager.syncState(state, bitmask); } -gl::Error Renderer9::setBlendState(const gl::Framebuffer *framebuffer, - const gl::BlendState &blendState, - const gl::ColorF &blendColor, - unsigned int sampleMask) +gl::Error Renderer9::updateState(const gl::Data &data, GLenum drawMode) { - bool blendStateChanged = mForceSetBlendState || memcmp(&blendState, &mCurBlendState, sizeof(gl::BlendState)) != 0; - bool blendColorChanged = mForceSetBlendState || memcmp(&blendColor, &mCurBlendColor, sizeof(gl::ColorF)) != 0; - bool sampleMaskChanged = mForceSetBlendState || sampleMask != mCurSampleMask; + // Applies the render target surface, depth stencil surface, viewport rectangle and + // scissor rectangle to the renderer + const gl::Framebuffer *framebufferObject = data.state->getDrawFramebuffer(); + ASSERT(framebufferObject && framebufferObject->checkStatus(data) == GL_FRAMEBUFFER_COMPLETE); - if (blendStateChanged || blendColorChanged) + gl::Error error = applyRenderTarget(framebufferObject); + if (error.isError()) { - if (blendState.blend) - { - mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); - - if (blendState.sourceBlendRGB != GL_CONSTANT_ALPHA && blendState.sourceBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA && - blendState.destBlendRGB != GL_CONSTANT_ALPHA && blendState.destBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA) - { - mDevice->SetRenderState(D3DRS_BLENDFACTOR, gl_d3d9::ConvertColor(blendColor)); - } - else - { - mDevice->SetRenderState(D3DRS_BLENDFACTOR, D3DCOLOR_RGBA(gl::unorm<8>(blendColor.alpha), - gl::unorm<8>(blendColor.alpha), - gl::unorm<8>(blendColor.alpha), - gl::unorm<8>(blendColor.alpha))); - } - - mDevice->SetRenderState(D3DRS_SRCBLEND, gl_d3d9::ConvertBlendFunc(blendState.sourceBlendRGB)); - mDevice->SetRenderState(D3DRS_DESTBLEND, gl_d3d9::ConvertBlendFunc(blendState.destBlendRGB)); - mDevice->SetRenderState(D3DRS_BLENDOP, gl_d3d9::ConvertBlendOp(blendState.blendEquationRGB)); - - if (blendState.sourceBlendRGB != blendState.sourceBlendAlpha || - blendState.destBlendRGB != blendState.destBlendAlpha || - blendState.blendEquationRGB != blendState.blendEquationAlpha) - { - mDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE); - - mDevice->SetRenderState(D3DRS_SRCBLENDALPHA, gl_d3d9::ConvertBlendFunc(blendState.sourceBlendAlpha)); - mDevice->SetRenderState(D3DRS_DESTBLENDALPHA, gl_d3d9::ConvertBlendFunc(blendState.destBlendAlpha)); - mDevice->SetRenderState(D3DRS_BLENDOPALPHA, gl_d3d9::ConvertBlendOp(blendState.blendEquationAlpha)); - } - else - { - mDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, FALSE); - } - } - else - { - mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); - } - - if (blendState.sampleAlphaToCoverage) - { - FIXME("Sample alpha to coverage is unimplemented."); - } - - const gl::FramebufferAttachment *attachment = framebuffer->getFirstColorbuffer(); - GLenum internalFormat = attachment ? attachment->getInternalFormat() : GL_NONE; - - // Set the color mask - bool zeroColorMaskAllowed = getVendorId() != VENDOR_ID_AMD; - // Apparently some ATI cards have a bug where a draw with a zero color - // write mask can cause later draws to have incorrect results. Instead, - // set a nonzero color write mask but modify the blend state so that no - // drawing is done. - // http://code.google.com/p/angleproject/issues/detail?id=169 - - const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat); - DWORD colorMask = gl_d3d9::ConvertColorMask(formatInfo.redBits > 0 && blendState.colorMaskRed, - formatInfo.greenBits > 0 && blendState.colorMaskGreen, - formatInfo.blueBits > 0 && blendState.colorMaskBlue, - formatInfo.alphaBits > 0 && blendState.colorMaskAlpha); - if (colorMask == 0 && !zeroColorMaskAllowed) - { - // Enable green channel, but set blending so nothing will be drawn. - mDevice->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_GREEN); - mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); - - mDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO); - mDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); - mDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); - } - else - { - mDevice->SetRenderState(D3DRS_COLORWRITEENABLE, colorMask); - } - - mDevice->SetRenderState(D3DRS_DITHERENABLE, blendState.dither ? TRUE : FALSE); - - mCurBlendState = blendState; - mCurBlendColor = blendColor; + return error; } - if (sampleMaskChanged) - { - // Set the multisample mask - mDevice->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, TRUE); - mDevice->SetRenderState(D3DRS_MULTISAMPLEMASK, static_cast(sampleMask)); + // Setting viewport state + setViewport(data.caps, data.state->getViewport(), data.state->getNearPlane(), + data.state->getFarPlane(), drawMode, data.state->getRasterizerState().frontFace, + false); - mCurSampleMask = sampleMask; + // Setting scissors state + setScissorRectangle(data.state->getScissor(), data.state->isScissorTestEnabled()); + + // Setting blend, depth stencil, and rasterizer states + int samples = framebufferObject->getSamples(data); + gl::RasterizerState rasterizer = data.state->getRasterizerState(); + rasterizer.pointDrawMode = (drawMode == GL_POINTS); + rasterizer.multiSample = (samples != 0); + + unsigned int mask = GetBlendSampleMask(data, samples); + error = setBlendDepthRasterStates(data, mask); + + if (error.isError()) + { + return error; } - mForceSetBlendState = false; + mStateManager.resetDirtyBits(); - return gl::Error(GL_NO_ERROR); -} - -gl::Error Renderer9::setDepthStencilState(const gl::DepthStencilState &depthStencilState, int stencilRef, - int stencilBackRef, bool frontFaceCCW) -{ - bool depthStencilStateChanged = mForceSetDepthStencilState || - memcmp(&depthStencilState, &mCurDepthStencilState, sizeof(gl::DepthStencilState)) != 0; - bool stencilRefChanged = mForceSetDepthStencilState || stencilRef != mCurStencilRef || - stencilBackRef != mCurStencilBackRef; - bool frontFaceCCWChanged = mForceSetDepthStencilState || frontFaceCCW != mCurFrontFaceCCW; - - if (depthStencilStateChanged) - { - if (depthStencilState.depthTest) - { - mDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE); - mDevice->SetRenderState(D3DRS_ZFUNC, gl_d3d9::ConvertComparison(depthStencilState.depthFunc)); - } - else - { - mDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); - } - - mCurDepthStencilState = depthStencilState; - } - - if (depthStencilStateChanged || stencilRefChanged || frontFaceCCWChanged) - { - if (depthStencilState.stencilTest && mCurStencilSize > 0) - { - mDevice->SetRenderState(D3DRS_STENCILENABLE, TRUE); - mDevice->SetRenderState(D3DRS_TWOSIDEDSTENCILMODE, TRUE); - - // FIXME: Unsupported by D3D9 - const D3DRENDERSTATETYPE D3DRS_CCW_STENCILREF = D3DRS_STENCILREF; - const D3DRENDERSTATETYPE D3DRS_CCW_STENCILMASK = D3DRS_STENCILMASK; - const D3DRENDERSTATETYPE D3DRS_CCW_STENCILWRITEMASK = D3DRS_STENCILWRITEMASK; - - // get the maximum size of the stencil ref - unsigned int maxStencil = (1 << mCurStencilSize) - 1; - - ASSERT((depthStencilState.stencilWritemask & maxStencil) == - (depthStencilState.stencilBackWritemask & maxStencil)); - ASSERT(stencilRef == stencilBackRef); - ASSERT((depthStencilState.stencilMask & maxStencil) == - (depthStencilState.stencilBackMask & maxStencil)); - - mDevice->SetRenderState(frontFaceCCW ? D3DRS_STENCILWRITEMASK : D3DRS_CCW_STENCILWRITEMASK, - depthStencilState.stencilWritemask); - mDevice->SetRenderState(frontFaceCCW ? D3DRS_STENCILFUNC : D3DRS_CCW_STENCILFUNC, - gl_d3d9::ConvertComparison(depthStencilState.stencilFunc)); - - mDevice->SetRenderState(frontFaceCCW ? D3DRS_STENCILREF : D3DRS_CCW_STENCILREF, - (stencilRef < (int)maxStencil) ? stencilRef : maxStencil); - mDevice->SetRenderState(frontFaceCCW ? D3DRS_STENCILMASK : D3DRS_CCW_STENCILMASK, - depthStencilState.stencilMask); - - mDevice->SetRenderState(frontFaceCCW ? D3DRS_STENCILFAIL : D3DRS_CCW_STENCILFAIL, - gl_d3d9::ConvertStencilOp(depthStencilState.stencilFail)); - mDevice->SetRenderState(frontFaceCCW ? D3DRS_STENCILZFAIL : D3DRS_CCW_STENCILZFAIL, - gl_d3d9::ConvertStencilOp(depthStencilState.stencilPassDepthFail)); - mDevice->SetRenderState(frontFaceCCW ? D3DRS_STENCILPASS : D3DRS_CCW_STENCILPASS, - gl_d3d9::ConvertStencilOp(depthStencilState.stencilPassDepthPass)); - - mDevice->SetRenderState(!frontFaceCCW ? D3DRS_STENCILWRITEMASK : D3DRS_CCW_STENCILWRITEMASK, - depthStencilState.stencilBackWritemask); - mDevice->SetRenderState(!frontFaceCCW ? D3DRS_STENCILFUNC : D3DRS_CCW_STENCILFUNC, - gl_d3d9::ConvertComparison(depthStencilState.stencilBackFunc)); - - mDevice->SetRenderState(!frontFaceCCW ? D3DRS_STENCILREF : D3DRS_CCW_STENCILREF, - (stencilBackRef < (int)maxStencil) ? stencilBackRef : maxStencil); - mDevice->SetRenderState(!frontFaceCCW ? D3DRS_STENCILMASK : D3DRS_CCW_STENCILMASK, - depthStencilState.stencilBackMask); - - mDevice->SetRenderState(!frontFaceCCW ? D3DRS_STENCILFAIL : D3DRS_CCW_STENCILFAIL, - gl_d3d9::ConvertStencilOp(depthStencilState.stencilBackFail)); - mDevice->SetRenderState(!frontFaceCCW ? D3DRS_STENCILZFAIL : D3DRS_CCW_STENCILZFAIL, - gl_d3d9::ConvertStencilOp(depthStencilState.stencilBackPassDepthFail)); - mDevice->SetRenderState(!frontFaceCCW ? D3DRS_STENCILPASS : D3DRS_CCW_STENCILPASS, - gl_d3d9::ConvertStencilOp(depthStencilState.stencilBackPassDepthPass)); - } - else - { - mDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE); - } - - mDevice->SetRenderState(D3DRS_ZWRITEENABLE, depthStencilState.depthMask ? TRUE : FALSE); - - mCurStencilRef = stencilRef; - mCurStencilBackRef = stencilBackRef; - mCurFrontFaceCCW = frontFaceCCW; - } - - mForceSetDepthStencilState = false; - - return gl::Error(GL_NO_ERROR); + return error; } void Renderer9::setScissorRectangle(const gl::Rectangle &scissor, bool enabled) { - bool scissorChanged = mForceSetScissor || - memcmp(&scissor, &mCurScissor, sizeof(gl::Rectangle)) != 0 || - enabled != mScissorEnabled; - - if (scissorChanged) - { - if (enabled) - { - RECT rect; - rect.left = gl::clamp(scissor.x, 0, static_cast(mRenderTargetDesc.width)); - rect.top = gl::clamp(scissor.y, 0, static_cast(mRenderTargetDesc.height)); - rect.right = gl::clamp(scissor.x + scissor.width, 0, static_cast(mRenderTargetDesc.width)); - rect.bottom = gl::clamp(scissor.y + scissor.height, 0, static_cast(mRenderTargetDesc.height)); - mDevice->SetScissorRect(&rect); - } - - mDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, enabled ? TRUE : FALSE); - - mScissorEnabled = enabled; - mCurScissor = scissor; - } - - mForceSetScissor = false; + mStateManager.setScissorState(scissor, enabled); } -void Renderer9::setViewport(const gl::Rectangle &viewport, float zNear, float zFar, GLenum drawMode, GLenum frontFace, +gl::Error Renderer9::setBlendDepthRasterStates(const gl::Data &glData, GLenum drawMode) +{ + int samples = glData.state->getDrawFramebuffer()->getSamples(glData); + gl::RasterizerState rasterizer = glData.state->getRasterizerState(); + rasterizer.pointDrawMode = (drawMode == GL_POINTS); + rasterizer.multiSample = (samples != 0); + + unsigned int mask = GetBlendSampleMask(glData, samples); + return mStateManager.setBlendDepthRasterStates(*glData.state, mask); +} + +void Renderer9::setViewport(const gl::Caps *caps, + const gl::Rectangle &viewport, + float zNear, + float zFar, + GLenum drawMode, + GLenum frontFace, bool ignoreViewport) { - gl::Rectangle actualViewport = viewport; - float actualZNear = gl::clamp01(zNear); - float actualZFar = gl::clamp01(zFar); - if (ignoreViewport) - { - actualViewport.x = 0; - actualViewport.y = 0; - actualViewport.width = static_cast(mRenderTargetDesc.width); - actualViewport.height = static_cast(mRenderTargetDesc.height); - actualZNear = 0.0f; - actualZFar = 1.0f; - } - - D3DVIEWPORT9 dxViewport; - dxViewport.X = gl::clamp(actualViewport.x, 0, static_cast(mRenderTargetDesc.width)); - dxViewport.Y = gl::clamp(actualViewport.y, 0, static_cast(mRenderTargetDesc.height)); - dxViewport.Width = gl::clamp(actualViewport.width, 0, static_cast(mRenderTargetDesc.width) - static_cast(dxViewport.X)); - dxViewport.Height = gl::clamp(actualViewport.height, 0, static_cast(mRenderTargetDesc.height) - static_cast(dxViewport.Y)); - dxViewport.MinZ = actualZNear; - dxViewport.MaxZ = actualZFar; - - float depthFront = !gl::IsTriangleMode(drawMode) ? 0.0f : (frontFace == GL_CCW ? 1.0f : -1.0f); - - bool viewportChanged = mForceSetViewport || memcmp(&actualViewport, &mCurViewport, sizeof(gl::Rectangle)) != 0 || - actualZNear != mCurNear || actualZFar != mCurFar || mCurDepthFront != depthFront; - if (viewportChanged) - { - mDevice->SetViewport(&dxViewport); - - mCurViewport = actualViewport; - mCurNear = actualZNear; - mCurFar = actualZFar; - mCurDepthFront = depthFront; - - dx_VertexConstants vc = {}; - dx_PixelConstants pc = {}; - - vc.viewAdjust[0] = (float)((actualViewport.width - (int)dxViewport.Width) + 2 * (actualViewport.x - (int)dxViewport.X) - 1) / dxViewport.Width; - vc.viewAdjust[1] = (float)((actualViewport.height - (int)dxViewport.Height) + 2 * (actualViewport.y - (int)dxViewport.Y) - 1) / dxViewport.Height; - vc.viewAdjust[2] = (float)actualViewport.width / dxViewport.Width; - vc.viewAdjust[3] = (float)actualViewport.height / dxViewport.Height; - - pc.viewCoords[0] = actualViewport.width * 0.5f; - pc.viewCoords[1] = actualViewport.height * 0.5f; - pc.viewCoords[2] = actualViewport.x + (actualViewport.width * 0.5f); - pc.viewCoords[3] = actualViewport.y + (actualViewport.height * 0.5f); - - pc.depthFront[0] = (actualZFar - actualZNear) * 0.5f; - pc.depthFront[1] = (actualZNear + actualZFar) * 0.5f; - pc.depthFront[2] = depthFront; - - vc.depthRange[0] = actualZNear; - vc.depthRange[1] = actualZFar; - vc.depthRange[2] = actualZFar - actualZNear; - - pc.depthRange[0] = actualZNear; - pc.depthRange[1] = actualZFar; - pc.depthRange[2] = actualZFar - actualZNear; - - if (memcmp(&vc, &mVertexConstants, sizeof(dx_VertexConstants)) != 0) - { - mVertexConstants = vc; - mDxUniformsDirty = true; - } - - if (memcmp(&pc, &mPixelConstants, sizeof(dx_PixelConstants)) != 0) - { - mPixelConstants = pc; - mDxUniformsDirty = true; - } - } - - mForceSetViewport = false; + mStateManager.setViewportState(caps, viewport, zNear, zFar, drawMode, frontFace, + ignoreViewport); } bool Renderer9::applyPrimitiveType(GLenum mode, GLsizei count, bool usesPointSize) @@ -1277,15 +1017,14 @@ gl::Error Renderer9::getNullColorbuffer(const gl::FramebufferAttachment *depthbu { ASSERT(depthbuffer); - GLsizei width = depthbuffer->getWidth(); - GLsizei height = depthbuffer->getHeight(); + const gl::Extents &size = depthbuffer->getSize(); // search cached nullcolorbuffers for (int i = 0; i < NUM_NULL_COLORBUFFER_CACHE_ENTRIES; i++) { if (mNullColorbufferCache[i].buffer != NULL && - mNullColorbufferCache[i].width == width && - mNullColorbufferCache[i].height == height) + mNullColorbufferCache[i].width == size.width && + mNullColorbufferCache[i].height == size.height) { mNullColorbufferCache[i].lruCount = ++mMaxNullColorbufferLRU; *outColorBuffer = mNullColorbufferCache[i].buffer; @@ -1294,7 +1033,7 @@ gl::Error Renderer9::getNullColorbuffer(const gl::FramebufferAttachment *depthbu } gl::Renderbuffer *nullRenderbuffer = new gl::Renderbuffer(createRenderbuffer(), 0); - gl::Error error = nullRenderbuffer->setStorage(GL_NONE, width, height); + gl::Error error = nullRenderbuffer->setStorage(GL_NONE, size.width, size.height); if (error.isError()) { SafeDelete(nullRenderbuffer); @@ -1316,8 +1055,8 @@ gl::Error Renderer9::getNullColorbuffer(const gl::FramebufferAttachment *depthbu delete oldest->buffer; oldest->buffer = nullbuffer; oldest->lruCount = ++mMaxNullColorbufferLRU; - oldest->width = width; - oldest->height = height; + oldest->width = size.width; + oldest->height = size.height; *outColorBuffer = nullbuffer; return gl::Error(GL_NO_ERROR); @@ -1409,17 +1148,8 @@ gl::Error Renderer9::applyRenderTarget(const gl::FramebufferAttachment *colorAtt mDevice->SetDepthStencilSurface(NULL); } - if (!mDepthStencilInitialized || depthSize != mCurDepthSize) - { - mCurDepthSize = depthSize; - mForceSetRasterState = true; - } - - if (!mDepthStencilInitialized || stencilSize != mCurStencilSize) - { - mCurStencilSize = stencilSize; - mForceSetDepthStencilState = true; - } + mStateManager.updateDepthSizeIfChanged(mDepthStencilInitialized, depthSize); + mStateManager.updateStencilSizeIfChanged(mDepthStencilInitialized, stencilSize); mAppliedDepthStencilSerial = depthStencilSerial; mDepthStencilInitialized = true; @@ -1427,13 +1157,9 @@ gl::Error Renderer9::applyRenderTarget(const gl::FramebufferAttachment *colorAtt if (renderTargetChanged || !mRenderTargetDescInitialized) { - mForceSetScissor = true; - mForceSetViewport = true; - mForceSetBlendState = true; - - mRenderTargetDesc.width = renderTargetWidth; - mRenderTargetDesc.height = renderTargetHeight; - mRenderTargetDesc.format = renderTargetFormat; + mStateManager.forceSetBlendState(); + mStateManager.forceSetScissorState(); + mStateManager.setRenderTargetBounds(renderTargetWidth, renderTargetHeight); mRenderTargetDescInitialized = true; } @@ -1445,7 +1171,12 @@ gl::Error Renderer9::applyRenderTarget(const gl::Framebuffer *framebuffer) return applyRenderTarget(framebuffer->getColorbuffer(0), framebuffer->getDepthOrStencilbuffer()); } -gl::Error Renderer9::applyVertexBuffer(const gl::State &state, GLenum mode, GLint first, GLsizei count, GLsizei instances, SourceIndexData * /*sourceInfo*/) +gl::Error Renderer9::applyVertexBuffer(const gl::State &state, + GLenum mode, + GLint first, + GLsizei count, + GLsizei instances, + TranslatedIndexData * /*indexInfo*/) { gl::Error error = mVertexDataManager->prepareVertexData(state, first, count, &mTranslatedAttribCache, instances); if (error.isError()) @@ -1462,13 +1193,12 @@ gl::Error Renderer9::applyIndexBuffer(const gl::Data &data, GLsizei count, GLenum mode, GLenum type, - TranslatedIndexData *indexInfo, - SourceIndexData *sourceIndexInfo) + TranslatedIndexData *indexInfo) { gl::VertexArray *vao = data.state->getVertexArray(); gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get(); gl::Error error = mIndexDataManager->prepareIndexData(type, count, elementArrayBuffer, indices, - indexInfo, sourceIndexInfo, false); + indexInfo, false); if (error.isError()) { return error; @@ -1918,7 +1648,7 @@ gl::Error Renderer9::applyShadersImpl(const gl::Data &data, GLenum /*drawMode*/) if (programSerial != mAppliedProgramSerial) { programD3D->dirtyAllUniforms(); - mDxUniformsDirty = true; + mStateManager.forceSetDXUniformsState(); mAppliedProgramSerial = programSerial; } @@ -1969,12 +1699,7 @@ gl::Error Renderer9::applyUniforms(const ProgramD3D &programD3D, } // Driver uniforms - if (mDxUniformsDirty) - { - mDevice->SetVertexShaderConstantF(0, (float*)&mVertexConstants, sizeof(dx_VertexConstants) / sizeof(float[4])); - mDevice->SetPixelShaderConstantF(0, (float*)&mPixelConstants, sizeof(dx_PixelConstants) / sizeof(float[4])); - mDxUniformsDirty = false; - } + mStateManager.setShaderConstants(); return gl::Error(GL_NO_ERROR); } @@ -2208,14 +1933,17 @@ gl::Error Renderer9::clear(const ClearParameters &clearParams, mDevice->SetStreamSourceFreq(i, 1); } + int renderTargetWidth = mStateManager.getRenderTargetWidth(); + int renderTargetHeight = mStateManager.getRenderTargetHeight(); + float quad[4][4]; // A quadrilateral covering the target, aligned to match the edges quad[0][0] = -0.5f; - quad[0][1] = mRenderTargetDesc.height - 0.5f; + quad[0][1] = renderTargetHeight - 0.5f; quad[0][2] = 0.0f; quad[0][3] = 1.0f; - quad[1][0] = mRenderTargetDesc.width - 0.5f; - quad[1][1] = mRenderTargetDesc.height - 0.5f; + quad[1][0] = renderTargetWidth - 0.5f; + quad[1][1] = renderTargetHeight - 0.5f; quad[1][2] = 0.0f; quad[1][3] = 1.0f; @@ -2224,7 +1952,7 @@ gl::Error Renderer9::clear(const ClearParameters &clearParams, quad[2][2] = 0.0f; quad[2][3] = 1.0f; - quad[3][0] = mRenderTargetDesc.width - 0.5f; + quad[3][0] = renderTargetWidth - 0.5f; quad[3][1] = -0.5f; quad[3][2] = 0.0f; quad[3][3] = 1.0f; @@ -2273,31 +2001,31 @@ void Renderer9::markAllStateDirty() mDepthStencilInitialized = false; mRenderTargetDescInitialized = false; - mForceSetDepthStencilState = true; - mForceSetRasterState = true; - mForceSetScissor = true; - mForceSetViewport = true; - mForceSetBlendState = true; + mStateManager.forceSetRasterState(); + mStateManager.forceSetDepthStencilState(); + mStateManager.forceSetBlendState(); + mStateManager.forceSetScissorState(); + mStateManager.forceSetViewportState(); ASSERT(mCurVertexSamplerStates.size() == mCurVertexTextures.size()); for (unsigned int i = 0; i < mCurVertexTextures.size(); i++) { mCurVertexSamplerStates[i].forceSet = true; - mCurVertexTextures[i] = DirtyPointer; + mCurVertexTextures[i] = angle::DirtyPointer; } ASSERT(mCurPixelSamplerStates.size() == mCurPixelTextures.size()); for (unsigned int i = 0; i < mCurPixelSamplerStates.size(); i++) { mCurPixelSamplerStates[i].forceSet = true; - mCurPixelTextures[i] = DirtyPointer; + mCurPixelTextures[i] = angle::DirtyPointer; } mAppliedIBSerial = 0; mAppliedVertexShader = NULL; mAppliedPixelShader = NULL; mAppliedProgramSerial = 0; - mDxUniformsDirty = true; + mStateManager.forceSetDXUniformsState(); mVertexDeclarationCache.markStateDirty(); } @@ -3052,14 +2780,23 @@ gl::Error Renderer9::clearTextures(gl::SamplerType samplerType, size_t rangeStar return gl::Error(GL_NO_ERROR); } -egl::Error Renderer9::initializeEGLDevice(DeviceD3D **outDevice) +egl::Error Renderer9::getEGLDevice(DeviceImpl **device) { - if (*outDevice == nullptr) + if (mEGLDevice == nullptr) { ASSERT(mDevice != nullptr); - *outDevice = new DeviceD3D(reinterpret_cast(mDevice), EGL_D3D9_DEVICE_ANGLE); + mEGLDevice = new DeviceD3D(); + egl::Error error = mEGLDevice->initialize(reinterpret_cast(mDevice), + EGL_D3D9_DEVICE_ANGLE, EGL_FALSE); + + if (error.isError()) + { + SafeDelete(mEGLDevice); + return error; + } } + *device = static_cast(mEGLDevice); return egl::Error(EGL_SUCCESS); } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h index 1845238891..a0dfecb02e 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h @@ -17,6 +17,7 @@ #include "libANGLE/renderer/d3d/d3d9/DebugAnnotator9.h" #include "libANGLE/renderer/d3d/d3d9/ShaderCache.h" #include "libANGLE/renderer/d3d/d3d9/VertexDeclarationCache.h" +#include "libANGLE/renderer/d3d/d3d9/StateManager9.h" namespace gl { @@ -78,7 +79,13 @@ class Renderer9 : public RendererD3D gl::Error flush() override; gl::Error finish() override; - virtual SwapChainD3D *createSwapChain(NativeWindow nativeWindow, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat); + SwapChainD3D *createSwapChain(NativeWindow nativeWindow, + HANDLE shareHandle, + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation) override; + + CompilerImpl *createCompiler() override; gl::Error allocateEventQuery(IDirect3DQuery9 **outQuery); void freeEventQuery(IDirect3DQuery9* query); @@ -96,17 +103,16 @@ class Renderer9 : public RendererD3D const std::vector &vertexUniformBuffers, const std::vector &fragmentUniformBuffers) override; - virtual gl::Error setRasterizerState(const gl::RasterizerState &rasterState); - gl::Error setBlendState(const gl::Framebuffer *framebuffer, - const gl::BlendState &blendState, - const gl::ColorF &blendColor, - unsigned int sampleMask) override; - virtual gl::Error setDepthStencilState(const gl::DepthStencilState &depthStencilState, int stencilRef, - int stencilBackRef, bool frontFaceCCW); + gl::Error updateState(const gl::Data &data, GLenum drawMode) override; - virtual void setScissorRectangle(const gl::Rectangle &scissor, bool enabled); - virtual void setViewport(const gl::Rectangle &viewport, float zNear, float zFar, GLenum drawMode, GLenum frontFace, - bool ignoreViewport); + void setScissorRectangle(const gl::Rectangle &scissor, bool enabled); + void setViewport(const gl::Caps *caps, + const gl::Rectangle &viewport, + float zNear, + float zFar, + GLenum drawMode, + GLenum frontFace, + bool ignoreViewport); gl::Error applyRenderTarget(const gl::Framebuffer *frameBuffer) override; gl::Error applyRenderTarget(const gl::FramebufferAttachment *colorAttachment, @@ -115,14 +121,18 @@ class Renderer9 : public RendererD3D GLenum drawMode, const std::vector &uniformArray) override; virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount, bool usesPointSize); - virtual gl::Error applyVertexBuffer(const gl::State &state, GLenum mode, GLint first, GLsizei count, GLsizei instances, SourceIndexData *sourceInfo); + virtual gl::Error applyVertexBuffer(const gl::State &state, + GLenum mode, + GLint first, + GLsizei count, + GLsizei instances, + TranslatedIndexData *indexInfo); gl::Error applyIndexBuffer(const gl::Data &data, const GLvoid *indices, GLsizei count, GLenum mode, GLenum type, - TranslatedIndexData *indexInfo, - SourceIndexData *sourceIndexInfo) override; + TranslatedIndexData *indexInfo) override; void applyTransformFeedbackBuffers(const gl::State &state) override; @@ -136,7 +146,7 @@ class Renderer9 : public RendererD3D bool testDeviceLost() override; bool testDeviceResettable() override; - VendorID getVendorId() const override; + VendorID getVendorId() const; std::string getRendererDescription() const override; DeviceIdentifier getAdapterIdentifier() const override; @@ -232,10 +242,7 @@ class Renderer9 : public RendererD3D virtual gl::Error fastCopyBufferToTexture(const gl::PixelUnpackState &unpack, unsigned int offset, RenderTargetD3D *destRenderTarget, GLenum destinationFormat, GLenum sourcePixelsType, const gl::Box &destArea); - void syncState(const gl::State & /*state*/, const gl::State::DirtyBits &bitmask) override - { - // TODO(dianx) implement d3d9 dirty bits - } + void syncState(const gl::State &state, const gl::State::DirtyBits &bitmask) override; // D3D9-renderer specific methods gl::Error boxFilter(IDirect3DSurface9 *source, IDirect3DSurface9 *dest); @@ -252,13 +259,13 @@ class Renderer9 : public RendererD3D D3DDEVTYPE getD3D9DeviceType() const { return mDeviceType; } + egl::Error getEGLDevice(DeviceImpl **device) override; + protected: void createAnnotator() override; gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd) override; gl::Error applyShadersImpl(const gl::Data &data, GLenum drawMode) override; - egl::Error initializeEGLDevice(DeviceD3D **outDevice) override; - private: gl::Error drawArraysImpl(const gl::Data &data, GLenum mode, @@ -278,6 +285,8 @@ class Renderer9 : public RendererD3D WorkaroundsD3D generateWorkarounds() const override; + gl::Error setBlendDepthRasterStates(const gl::Data &glData, GLenum drawMode); + void release(); void applyUniformnfv(const D3DUniform *targetUniform, const GLfloat *v); @@ -332,43 +341,10 @@ class Renderer9 : public RendererD3D unsigned int mAppliedDepthStencilSerial; bool mDepthStencilInitialized; bool mRenderTargetDescInitialized; - unsigned int mCurStencilSize; - unsigned int mCurDepthSize; - - struct RenderTargetDesc - { - size_t width; - size_t height; - D3DFORMAT format; - }; - RenderTargetDesc mRenderTargetDesc; IDirect3DStateBlock9 *mMaskedClearSavedState; - // previously set render states - bool mForceSetDepthStencilState; - gl::DepthStencilState mCurDepthStencilState; - int mCurStencilRef; - int mCurStencilBackRef; - bool mCurFrontFaceCCW; - - bool mForceSetRasterState; - gl::RasterizerState mCurRasterState; - - bool mForceSetScissor; - gl::Rectangle mCurScissor; - bool mScissorEnabled; - - bool mForceSetViewport; - gl::Rectangle mCurViewport; - float mCurNear; - float mCurFar; - float mCurDepthFront; - - bool mForceSetBlendState; - gl::BlendState mCurBlendState; - gl::ColorF mCurBlendColor; - GLuint mCurSampleMask; + StateManager9 mStateManager; // Currently applied sampler states struct CurSamplerState @@ -391,10 +367,6 @@ class Renderer9 : public RendererD3D IDirect3DPixelShader9 *mAppliedPixelShader; unsigned int mAppliedProgramSerial; - dx_VertexConstants mVertexConstants; - dx_PixelConstants mPixelConstants; - bool mDxUniformsDirty; - // A pool of event queries that are currently unused. std::vector mEventQueryPool; VertexShaderCache mVertexShaderCache; @@ -416,6 +388,8 @@ class Renderer9 : public RendererD3D gl::FramebufferAttachment *buffer; } mNullColorbufferCache[NUM_NULL_COLORBUFFER_CACHE_ENTRIES]; UINT mMaxNullColorbufferLRU; + + DeviceD3D *mEGLDevice; }; } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/StateManager9.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/StateManager9.cpp new file mode 100644 index 0000000000..c4c600aedb --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/StateManager9.cpp @@ -0,0 +1,903 @@ +// +// Copyright (c) 2015 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. +// + +// StateManager9.cpp: Defines a class for caching D3D9 state +#include "libANGLE/renderer/d3d/d3d9/StateManager9.h" + +#include "common/BitSetIterator.h" +#include "common/utilities.h" +#include "libANGLE/formatutils.h" +#include "libANGLE/renderer/d3d/d3d9/renderer9_utils.h" +#include "libANGLE/renderer/d3d/d3d9/Framebuffer9.h" +#include "libANGLE/renderer/d3d/d3d9/Renderer9.h" + +namespace rx +{ + +StateManager9::StateManager9(Renderer9 *renderer9) + : mCurBlendState(), + mCurBlendColor(0, 0, 0, 0), + mCurSampleMask(0), + mCurRasterState(), + mCurDepthSize(0), + mCurDepthStencilState(), + mCurStencilRef(0), + mCurStencilBackRef(0), + mCurFrontFaceCCW(0), + mCurStencilSize(0), + mCurScissorRect(), + mCurScissorEnabled(false), + mCurViewport(), + mCurNear(0.0f), + mCurFar(0.0f), + mCurDepthFront(0.0f), + mCurIgnoreViewport(false), + mRenderer9(renderer9), + mDirtyBits() +{ + mBlendStateDirtyBits.set(DIRTY_BIT_BLEND_ENABLED); + mBlendStateDirtyBits.set(DIRTY_BIT_BLEND_COLOR); + mBlendStateDirtyBits.set(DIRTY_BIT_BLEND_FUNCS_EQUATIONS); + mBlendStateDirtyBits.set(DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE); + mBlendStateDirtyBits.set(DIRTY_BIT_COLOR_MASK); + mBlendStateDirtyBits.set(DIRTY_BIT_DITHER); + mBlendStateDirtyBits.set(DIRTY_BIT_SAMPLE_MASK); + + mRasterizerStateDirtyBits.set(DIRTY_BIT_CULL_MODE); + mRasterizerStateDirtyBits.set(DIRTY_BIT_DEPTH_BIAS); + + mDepthStencilStateDirtyBits.set(DIRTY_BIT_STENCIL_DEPTH_MASK); + mDepthStencilStateDirtyBits.set(DIRTY_BIT_STENCIL_DEPTH_FUNC); + mDepthStencilStateDirtyBits.set(DIRTY_BIT_STENCIL_TEST_ENABLED); + mDepthStencilStateDirtyBits.set(DIRTY_BIT_STENCIL_FUNCS_FRONT); + mDepthStencilStateDirtyBits.set(DIRTY_BIT_STENCIL_FUNCS_BACK); + mDepthStencilStateDirtyBits.set(DIRTY_BIT_STENCIL_WRITEMASK_FRONT); + mDepthStencilStateDirtyBits.set(DIRTY_BIT_STENCIL_WRITEMASK_BACK); + mDepthStencilStateDirtyBits.set(DIRTY_BIT_STENCIL_OPS_FRONT); + mDepthStencilStateDirtyBits.set(DIRTY_BIT_STENCIL_OPS_BACK); + + mScissorStateDirtyBits.set(DIRTY_BIT_SCISSOR_ENABLED); + mScissorStateDirtyBits.set(DIRTY_BIT_SCISSOR_RECT); +} + +StateManager9::~StateManager9() +{ +} + +void StateManager9::forceSetBlendState() +{ + mDirtyBits |= mBlendStateDirtyBits; +} + +void StateManager9::forceSetRasterState() +{ + mDirtyBits |= mRasterizerStateDirtyBits; +} + +void StateManager9::forceSetDepthStencilState() +{ + mDirtyBits |= mDepthStencilStateDirtyBits; +} + +void StateManager9::forceSetScissorState() +{ + mDirtyBits |= mScissorStateDirtyBits; +} + +void StateManager9::forceSetViewportState() +{ + mForceSetViewport = true; +} + +void StateManager9::forceSetDXUniformsState() +{ + mDxUniformsDirty = true; +} + +void StateManager9::updateStencilSizeIfChanged(bool depthStencilInitialized, + unsigned int stencilSize) +{ + if (!depthStencilInitialized || stencilSize != mCurStencilSize) + { + mCurStencilSize = stencilSize; + forceSetDepthStencilState(); + } +} + +void StateManager9::syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits) +{ + if (!dirtyBits.any()) + { + return; + } + + for (auto dirtyBit : angle::IterateBitSet(dirtyBits)) + { + switch (dirtyBit) + { + case gl::State::DIRTY_BIT_BLEND_ENABLED: + if (state.getBlendState().blend != mCurBlendState.blend) + { + mDirtyBits.set(DIRTY_BIT_BLEND_ENABLED); + // BlendColor and funcs and equations has to be set if blend is enabled + mDirtyBits.set(DIRTY_BIT_BLEND_COLOR); + mDirtyBits.set(DIRTY_BIT_BLEND_FUNCS_EQUATIONS); + } + break; + case gl::State::DIRTY_BIT_BLEND_FUNCS: + { + const gl::BlendState &blendState = state.getBlendState(); + if (blendState.sourceBlendRGB != mCurBlendState.sourceBlendRGB || + blendState.destBlendRGB != mCurBlendState.destBlendRGB || + blendState.sourceBlendAlpha != mCurBlendState.sourceBlendAlpha || + blendState.destBlendAlpha != mCurBlendState.destBlendAlpha) + { + mDirtyBits.set(DIRTY_BIT_BLEND_FUNCS_EQUATIONS); + // BlendColor depends on the values of blend funcs + mDirtyBits.set(DIRTY_BIT_BLEND_COLOR); + } + break; + } + case gl::State::DIRTY_BIT_BLEND_EQUATIONS: + { + const gl::BlendState &blendState = state.getBlendState(); + if (blendState.blendEquationRGB != mCurBlendState.blendEquationRGB || + blendState.blendEquationAlpha != mCurBlendState.blendEquationAlpha) + { + mDirtyBits.set(DIRTY_BIT_BLEND_FUNCS_EQUATIONS); + } + break; + } + case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED: + if (state.getBlendState().sampleAlphaToCoverage != + mCurBlendState.sampleAlphaToCoverage) + { + mDirtyBits.set(DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE); + } + break; + case gl::State::DIRTY_BIT_COLOR_MASK: + { + const gl::BlendState &blendState = state.getBlendState(); + if (blendState.colorMaskRed != mCurBlendState.colorMaskRed || + blendState.colorMaskGreen != mCurBlendState.colorMaskGreen || + blendState.colorMaskBlue != mCurBlendState.colorMaskBlue || + blendState.colorMaskAlpha != mCurBlendState.colorMaskAlpha) + { + mDirtyBits.set(DIRTY_BIT_COLOR_MASK); + } + break; + } + case gl::State::DIRTY_BIT_DITHER_ENABLED: + if (state.getBlendState().dither != mCurBlendState.dither) + { + mDirtyBits.set(DIRTY_BIT_DITHER); + } + break; + case gl::State::DIRTY_BIT_BLEND_COLOR: + if (state.getBlendColor() != mCurBlendColor) + { + mDirtyBits.set(DIRTY_BIT_BLEND_COLOR); + } + break; + case gl::State::DIRTY_BIT_CULL_FACE_ENABLED: + if (state.getRasterizerState().cullFace != mCurRasterState.cullFace) + { + mDirtyBits.set(DIRTY_BIT_CULL_MODE); + } + break; + case gl::State::DIRTY_BIT_CULL_FACE: + if (state.getRasterizerState().cullMode != mCurRasterState.cullMode) + { + mDirtyBits.set(DIRTY_BIT_CULL_MODE); + } + break; + case gl::State::DIRTY_BIT_FRONT_FACE: + if (state.getRasterizerState().frontFace != mCurRasterState.frontFace) + { + mDirtyBits.set(DIRTY_BIT_CULL_MODE); + + // Viewport state depends on rasterizer.frontface + mDirtyBits.set(DIRTY_BIT_VIEWPORT); + } + break; + case gl::State::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED: + if (state.getRasterizerState().polygonOffsetFill != + mCurRasterState.polygonOffsetFill) + { + mDirtyBits.set(DIRTY_BIT_DEPTH_BIAS); + } + break; + case gl::State::DIRTY_BIT_POLYGON_OFFSET: + { + const gl::RasterizerState &rasterizerState = state.getRasterizerState(); + if (rasterizerState.polygonOffsetFactor != mCurRasterState.polygonOffsetFactor || + rasterizerState.polygonOffsetUnits != mCurRasterState.polygonOffsetUnits) + { + mDirtyBits.set(DIRTY_BIT_DEPTH_BIAS); + } + } + case gl::State::DIRTY_BIT_DEPTH_MASK: + if (state.getDepthStencilState().depthMask != mCurDepthStencilState.depthMask) + { + mDirtyBits.set(DIRTY_BIT_STENCIL_DEPTH_MASK); + } + break; + case gl::State::DIRTY_BIT_DEPTH_TEST_ENABLED: + if (state.getDepthStencilState().depthTest != mCurDepthStencilState.depthTest) + { + mDirtyBits.set(DIRTY_BIT_STENCIL_DEPTH_FUNC); + } + break; + case gl::State::DIRTY_BIT_DEPTH_FUNC: + if (state.getDepthStencilState().depthFunc != mCurDepthStencilState.depthFunc) + { + mDirtyBits.set(DIRTY_BIT_STENCIL_DEPTH_FUNC); + } + break; + case gl::State::DIRTY_BIT_STENCIL_TEST_ENABLED: + if (state.getDepthStencilState().stencilTest != mCurDepthStencilState.stencilTest) + { + mDirtyBits.set(DIRTY_BIT_STENCIL_TEST_ENABLED); + // If we enable the stencil test, all of these must be set + mDirtyBits.set(DIRTY_BIT_STENCIL_WRITEMASK_BACK); + mDirtyBits.set(DIRTY_BIT_STENCIL_WRITEMASK_FRONT); + mDirtyBits.set(DIRTY_BIT_STENCIL_FUNCS_FRONT); + mDirtyBits.set(DIRTY_BIT_STENCIL_FUNCS_BACK); + mDirtyBits.set(DIRTY_BIT_STENCIL_OPS_FRONT); + mDirtyBits.set(DIRTY_BIT_STENCIL_OPS_BACK); + } + break; + case gl::State::DIRTY_BIT_STENCIL_FUNCS_FRONT: + { + const gl::DepthStencilState &depthStencilState = state.getDepthStencilState(); + if (depthStencilState.stencilFunc != mCurDepthStencilState.stencilFunc || + depthStencilState.stencilMask != mCurDepthStencilState.stencilMask || + state.getStencilRef() != mCurStencilRef) + { + mDirtyBits.set(DIRTY_BIT_STENCIL_FUNCS_FRONT); + } + break; + } + case gl::State::DIRTY_BIT_STENCIL_FUNCS_BACK: + { + const gl::DepthStencilState &depthStencilState = state.getDepthStencilState(); + if (depthStencilState.stencilBackFunc != mCurDepthStencilState.stencilBackFunc || + depthStencilState.stencilBackMask != mCurDepthStencilState.stencilBackMask || + state.getStencilBackRef() != mCurStencilBackRef) + { + mDirtyBits.set(DIRTY_BIT_STENCIL_FUNCS_BACK); + } + break; + } + case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_FRONT: + if (state.getDepthStencilState().stencilWritemask != + mCurDepthStencilState.stencilWritemask) + { + mDirtyBits.set(DIRTY_BIT_STENCIL_WRITEMASK_FRONT); + } + break; + case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_BACK: + if (state.getDepthStencilState().stencilBackWritemask != + mCurDepthStencilState.stencilBackWritemask) + { + mDirtyBits.set(DIRTY_BIT_STENCIL_WRITEMASK_BACK); + } + break; + case gl::State::DIRTY_BIT_STENCIL_OPS_FRONT: + { + const gl::DepthStencilState &depthStencilState = state.getDepthStencilState(); + if (depthStencilState.stencilFail != mCurDepthStencilState.stencilFail || + depthStencilState.stencilPassDepthFail != + mCurDepthStencilState.stencilPassDepthFail || + depthStencilState.stencilPassDepthPass != + mCurDepthStencilState.stencilPassDepthPass) + { + mDirtyBits.set(DIRTY_BIT_STENCIL_OPS_FRONT); + } + break; + } + case gl::State::DIRTY_BIT_STENCIL_OPS_BACK: + { + const gl::DepthStencilState &depthStencilState = state.getDepthStencilState(); + if (depthStencilState.stencilBackFail != mCurDepthStencilState.stencilBackFail || + depthStencilState.stencilBackPassDepthFail != + mCurDepthStencilState.stencilBackPassDepthFail || + depthStencilState.stencilBackPassDepthPass != + mCurDepthStencilState.stencilBackPassDepthPass) + { + mDirtyBits.set(DIRTY_BIT_STENCIL_OPS_BACK); + } + break; + } + case gl::State::DIRTY_BIT_SCISSOR_TEST_ENABLED: + if (state.isScissorTestEnabled() != mCurScissorEnabled) + { + mDirtyBits.set(DIRTY_BIT_SCISSOR_ENABLED); + // If scissor is enabled, we have to set the scissor rect + mDirtyBits.set(DIRTY_BIT_SCISSOR_RECT); + } + break; + case gl::State::DIRTY_BIT_SCISSOR: + if (state.getScissor() != mCurScissorRect) + { + mDirtyBits.set(DIRTY_BIT_SCISSOR_RECT); + } + break; + case gl::State::DIRTY_BIT_DEPTH_RANGE: + if (state.getNearPlane() != mCurNear || state.getFarPlane() != mCurFar) + { + mDirtyBits.set(DIRTY_BIT_VIEWPORT); + } + break; + case gl::State::DIRTY_BIT_VIEWPORT: + if (state.getViewport() != mCurViewport) + { + mDirtyBits.set(DIRTY_BIT_VIEWPORT); + } + break; + default: + break; + } + } +} + +gl::Error StateManager9::setBlendDepthRasterStates(const gl::State &glState, + unsigned int sampleMask) +{ + const gl::Framebuffer *framebuffer = glState.getDrawFramebuffer(); + + const gl::BlendState &blendState = glState.getBlendState(); + const gl::ColorF &blendColor = glState.getBlendColor(); + const gl::RasterizerState &rasterState = glState.getRasterizerState(); + + const auto &depthStencilState = glState.getDepthStencilState(); + bool frontFaceCCW = (glState.getRasterizerState().frontFace == GL_CCW); + unsigned int maxStencil = (1 << mCurStencilSize) - 1; + + // All the depth stencil states depends on the front face ccw variable + if (frontFaceCCW != mCurFrontFaceCCW) + { + forceSetDepthStencilState(); + mCurFrontFaceCCW = frontFaceCCW; + } + + for (auto dirtyBit : angle::IterateBitSet(mDirtyBits)) + { + switch (dirtyBit) + { + case DIRTY_BIT_BLEND_ENABLED: + setBlendEnabled(blendState.blend); + break; + case DIRTY_BIT_BLEND_COLOR: + setBlendColor(blendState, blendColor); + break; + case DIRTY_BIT_BLEND_FUNCS_EQUATIONS: + setBlendFuncsEquations(blendState); + break; + case DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE: + setSampleAlphaToCoverage(blendState.sampleAlphaToCoverage); + break; + case DIRTY_BIT_COLOR_MASK: + setColorMask(framebuffer, blendState.colorMaskRed, blendState.colorMaskBlue, + blendState.colorMaskGreen, blendState.colorMaskAlpha); + break; + case DIRTY_BIT_DITHER: + setDither(blendState.dither); + break; + case DIRTY_BIT_CULL_MODE: + setCullMode(rasterState.cullFace, rasterState.cullMode, rasterState.frontFace); + break; + case DIRTY_BIT_DEPTH_BIAS: + setDepthBias(rasterState.polygonOffsetFill, rasterState.polygonOffsetFactor, + rasterState.polygonOffsetUnits); + break; + case DIRTY_BIT_STENCIL_DEPTH_MASK: + setDepthMask(depthStencilState.depthMask); + break; + case DIRTY_BIT_STENCIL_DEPTH_FUNC: + setDepthFunc(depthStencilState.depthTest, depthStencilState.depthFunc); + break; + case DIRTY_BIT_STENCIL_TEST_ENABLED: + setStencilTestEnabled(depthStencilState.stencilTest); + break; + case DIRTY_BIT_STENCIL_FUNCS_FRONT: + setStencilFuncsFront(depthStencilState.stencilFunc, depthStencilState.stencilMask, + glState.getStencilRef(), frontFaceCCW, maxStencil); + break; + case DIRTY_BIT_STENCIL_FUNCS_BACK: + setStencilFuncsBack(depthStencilState.stencilBackFunc, + depthStencilState.stencilBackMask, glState.getStencilBackRef(), + frontFaceCCW, maxStencil); + break; + case DIRTY_BIT_STENCIL_WRITEMASK_FRONT: + setStencilWriteMask(depthStencilState.stencilWritemask, frontFaceCCW); + break; + case DIRTY_BIT_STENCIL_WRITEMASK_BACK: + setStencilBackWriteMask(depthStencilState.stencilBackWritemask, frontFaceCCW); + break; + case DIRTY_BIT_STENCIL_OPS_FRONT: + setStencilOpsFront(depthStencilState.stencilFail, + depthStencilState.stencilPassDepthFail, + depthStencilState.stencilPassDepthPass, frontFaceCCW); + break; + case DIRTY_BIT_STENCIL_OPS_BACK: + setStencilOpsBack(depthStencilState.stencilBackFail, + depthStencilState.stencilBackPassDepthFail, + depthStencilState.stencilBackPassDepthPass, frontFaceCCW); + break; + default: + break; + } + } + + if (sampleMask != mCurSampleMask) + { + setSampleMask(sampleMask); + } + + return gl::Error(GL_NO_ERROR); +} + +void StateManager9::setViewportState(const gl::Caps *caps, + const gl::Rectangle &viewport, + float zNear, + float zFar, + GLenum drawMode, + GLenum frontFace, + bool ignoreViewport) +{ + if (!mDirtyBits.test(DIRTY_BIT_VIEWPORT) && mCurIgnoreViewport == ignoreViewport) + return; + + gl::Rectangle actualViewport = viewport; + float actualZNear = gl::clamp01(zNear); + float actualZFar = gl::clamp01(zFar); + + if (ignoreViewport) + { + actualViewport.x = 0; + actualViewport.y = 0; + actualViewport.width = static_cast(mRenderTargetBounds.width); + actualViewport.height = static_cast(mRenderTargetBounds.height); + actualZNear = 0.0f; + actualZFar = 1.0f; + } + + D3DVIEWPORT9 dxViewport; + dxViewport.X = gl::clamp(actualViewport.x, 0, static_cast(mRenderTargetBounds.width)); + dxViewport.Y = gl::clamp(actualViewport.y, 0, static_cast(mRenderTargetBounds.height)); + dxViewport.Width = + gl::clamp(actualViewport.width, 0, + static_cast(mRenderTargetBounds.width) - static_cast(dxViewport.X)); + dxViewport.Height = + gl::clamp(actualViewport.height, 0, + static_cast(mRenderTargetBounds.height) - static_cast(dxViewport.Y)); + dxViewport.MinZ = actualZNear; + dxViewport.MaxZ = actualZFar; + + float depthFront = !gl::IsTriangleMode(drawMode) ? 0.0f : (frontFace == GL_CCW ? 1.0f : -1.0f); + + mRenderer9->getDevice()->SetViewport(&dxViewport); + + mCurViewport = actualViewport; + mCurNear = actualZNear; + mCurFar = actualZFar; + mCurDepthFront = depthFront; + mCurIgnoreViewport = ignoreViewport; + + // Setting shader constants + dx_VertexConstants9 vc = {}; + dx_PixelConstants9 pc = {}; + + vc.viewAdjust[0] = + static_cast((actualViewport.width - static_cast(dxViewport.Width)) + + 2 * (actualViewport.x - static_cast(dxViewport.X)) - 1) / + dxViewport.Width; + vc.viewAdjust[1] = + static_cast((actualViewport.height - static_cast(dxViewport.Height)) + + 2 * (actualViewport.y - static_cast(dxViewport.Y)) - 1) / + dxViewport.Height; + vc.viewAdjust[2] = static_cast(actualViewport.width) / dxViewport.Width; + vc.viewAdjust[3] = static_cast(actualViewport.height) / dxViewport.Height; + + pc.viewCoords[0] = actualViewport.width * 0.5f; + pc.viewCoords[1] = actualViewport.height * 0.5f; + pc.viewCoords[2] = actualViewport.x + (actualViewport.width * 0.5f); + pc.viewCoords[3] = actualViewport.y + (actualViewport.height * 0.5f); + + pc.depthFront[0] = (actualZFar - actualZNear) * 0.5f; + pc.depthFront[1] = (actualZNear + actualZFar) * 0.5f; + pc.depthFront[2] = depthFront; + + vc.depthRange[0] = actualZNear; + vc.depthRange[1] = actualZFar; + vc.depthRange[2] = actualZFar - actualZNear; + + pc.depthRange[0] = actualZNear; + pc.depthRange[1] = actualZFar; + pc.depthRange[2] = actualZFar - actualZNear; + + if (memcmp(&vc, &mVertexConstants, sizeof(dx_VertexConstants9)) != 0) + { + mVertexConstants = vc; + mDxUniformsDirty = true; + } + + if (memcmp(&pc, &mPixelConstants, sizeof(dx_PixelConstants9)) != 0) + { + mPixelConstants = pc; + mDxUniformsDirty = true; + } + + mForceSetViewport = false; +} + +void StateManager9::setShaderConstants() +{ + if (!mDxUniformsDirty) + return; + + IDirect3DDevice9 *device = mRenderer9->getDevice(); + device->SetVertexShaderConstantF(0, reinterpret_cast(&mVertexConstants), + sizeof(dx_VertexConstants9) / sizeof(float[4])); + device->SetPixelShaderConstantF(0, reinterpret_cast(&mPixelConstants), + sizeof(dx_PixelConstants9) / sizeof(float[4])); + mDxUniformsDirty = false; +} + +// This is separate from the main state loop because other functions +// outside call only setScissorState to update scissor state +void StateManager9::setScissorState(const gl::Rectangle &scissor, bool enabled) +{ + if (mDirtyBits.test(DIRTY_BIT_SCISSOR_ENABLED)) + setScissorEnabled(enabled); + + if (mDirtyBits.test(DIRTY_BIT_SCISSOR_RECT)) + setScissorRect(scissor, enabled); +} + +void StateManager9::setRenderTargetBounds(size_t width, size_t height) +{ + mRenderTargetBounds.width = (int)width; + mRenderTargetBounds.height = (int)height; + forceSetViewportState(); +} + +void StateManager9::setScissorEnabled(bool scissorEnabled) +{ + mRenderer9->getDevice()->SetRenderState(D3DRS_SCISSORTESTENABLE, scissorEnabled ? TRUE : FALSE); + mCurScissorEnabled = scissorEnabled; +} + +void StateManager9::setScissorRect(const gl::Rectangle &scissor, bool enabled) +{ + if (!enabled) + return; + + RECT rect; + rect.left = gl::clamp(scissor.x, 0, static_cast(mRenderTargetBounds.width)); + rect.top = gl::clamp(scissor.y, 0, static_cast(mRenderTargetBounds.height)); + rect.right = + gl::clamp(scissor.x + scissor.width, 0, static_cast(mRenderTargetBounds.width)); + rect.bottom = + gl::clamp(scissor.y + scissor.height, 0, static_cast(mRenderTargetBounds.height)); + mRenderer9->getDevice()->SetScissorRect(&rect); +} + +void StateManager9::setDepthFunc(bool depthTest, GLenum depthFunc) +{ + if (depthTest) + { + IDirect3DDevice9 *device = mRenderer9->getDevice(); + device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE); + device->SetRenderState(D3DRS_ZFUNC, gl_d3d9::ConvertComparison(depthFunc)); + } + else + { + mRenderer9->getDevice()->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); + } + + mCurDepthStencilState.depthTest = depthTest; + mCurDepthStencilState.depthFunc = depthFunc; +} + +void StateManager9::setStencilOpsFront(GLenum stencilFail, + GLenum stencilPassDepthFail, + GLenum stencilPassDepthPass, + bool frontFaceCCW) +{ + // TODO(dianx) It may be slightly more efficient todo these and other similar areas + // with separate dirty bits. + IDirect3DDevice9 *device = mRenderer9->getDevice(); + device->SetRenderState(frontFaceCCW ? D3DRS_STENCILFAIL : D3DRS_CCW_STENCILFAIL, + gl_d3d9::ConvertStencilOp(stencilFail)); + device->SetRenderState(frontFaceCCW ? D3DRS_STENCILZFAIL : D3DRS_CCW_STENCILZFAIL, + gl_d3d9::ConvertStencilOp(stencilPassDepthFail)); + device->SetRenderState(frontFaceCCW ? D3DRS_STENCILPASS : D3DRS_CCW_STENCILPASS, + gl_d3d9::ConvertStencilOp(stencilPassDepthPass)); + + mCurDepthStencilState.stencilFail = stencilFail; + mCurDepthStencilState.stencilPassDepthFail = stencilPassDepthFail; + mCurDepthStencilState.stencilPassDepthPass = stencilPassDepthPass; +} + +void StateManager9::setStencilOpsBack(GLenum stencilBackFail, + GLenum stencilBackPassDepthFail, + GLenum stencilBackPassDepthPass, + bool frontFaceCCW) +{ + IDirect3DDevice9 *device = mRenderer9->getDevice(); + device->SetRenderState(!frontFaceCCW ? D3DRS_STENCILFAIL : D3DRS_CCW_STENCILFAIL, + gl_d3d9::ConvertStencilOp(stencilBackFail)); + device->SetRenderState(!frontFaceCCW ? D3DRS_STENCILZFAIL : D3DRS_CCW_STENCILZFAIL, + gl_d3d9::ConvertStencilOp(stencilBackPassDepthFail)); + device->SetRenderState(!frontFaceCCW ? D3DRS_STENCILPASS : D3DRS_CCW_STENCILPASS, + gl_d3d9::ConvertStencilOp(stencilBackPassDepthPass)); + + mCurDepthStencilState.stencilBackFail = stencilBackFail; + mCurDepthStencilState.stencilBackPassDepthFail = stencilBackPassDepthFail; + mCurDepthStencilState.stencilBackPassDepthPass = stencilBackPassDepthPass; +} + +void StateManager9::setStencilBackWriteMask(GLuint stencilBackWriteMask, bool frontFaceCCW) +{ + mRenderer9->getDevice()->SetRenderState( + !frontFaceCCW ? D3DRS_STENCILWRITEMASK : D3DRS_CCW_STENCILWRITEMASK, stencilBackWriteMask); + + mCurDepthStencilState.stencilBackWritemask = stencilBackWriteMask; +} + +void StateManager9::setStencilFuncsBack(GLenum stencilBackFunc, + GLuint stencilBackMask, + GLint stencilBackRef, + bool frontFaceCCW, + unsigned int maxStencil) +{ + IDirect3DDevice9 *device = mRenderer9->getDevice(); + device->SetRenderState(!frontFaceCCW ? D3DRS_STENCILFUNC : D3DRS_CCW_STENCILFUNC, + gl_d3d9::ConvertComparison(stencilBackFunc)); + device->SetRenderState(!frontFaceCCW ? D3DRS_STENCILREF : D3DRS_CCW_STENCILREF, + (stencilBackRef < (int)maxStencil) ? stencilBackRef : maxStencil); + device->SetRenderState(!frontFaceCCW ? D3DRS_STENCILMASK : D3DRS_CCW_STENCILMASK, + stencilBackMask); + + mCurDepthStencilState.stencilBackFunc = stencilBackFunc; + mCurStencilBackRef = stencilBackRef; + mCurDepthStencilState.stencilBackMask = stencilBackMask; +} + +void StateManager9::setStencilWriteMask(GLuint stencilWriteMask, bool frontFaceCCW) +{ + mRenderer9->getDevice()->SetRenderState( + frontFaceCCW ? D3DRS_STENCILWRITEMASK : D3DRS_CCW_STENCILWRITEMASK, stencilWriteMask); + mCurDepthStencilState.stencilWritemask = stencilWriteMask; +} + +void StateManager9::setStencilFuncsFront(GLenum stencilFunc, + GLuint stencilMask, + GLint stencilRef, + bool frontFaceCCW, + unsigned int maxStencil) +{ + IDirect3DDevice9 *device = mRenderer9->getDevice(); + device->SetRenderState(frontFaceCCW ? D3DRS_STENCILFUNC : D3DRS_CCW_STENCILFUNC, + gl_d3d9::ConvertComparison(stencilFunc)); + device->SetRenderState(frontFaceCCW ? D3DRS_STENCILREF : D3DRS_CCW_STENCILREF, + (stencilRef < static_cast(maxStencil)) ? stencilRef : maxStencil); + device->SetRenderState(frontFaceCCW ? D3DRS_STENCILMASK : D3DRS_CCW_STENCILMASK, stencilMask); + + mCurDepthStencilState.stencilFunc = stencilFunc; + mCurStencilRef = stencilRef; + mCurDepthStencilState.stencilMask = stencilMask; +} +void StateManager9::setStencilTestEnabled(bool stencilTestEnabled) +{ + if (stencilTestEnabled && mCurStencilSize > 0) + { + mRenderer9->getDevice()->SetRenderState(D3DRS_STENCILENABLE, TRUE); + } + else + { + mRenderer9->getDevice()->SetRenderState(D3DRS_STENCILENABLE, FALSE); + } + + mCurDepthStencilState.stencilTest = stencilTestEnabled; +} + +void StateManager9::setDepthMask(bool depthMask) +{ + mRenderer9->getDevice()->SetRenderState(D3DRS_ZWRITEENABLE, depthMask ? TRUE : FALSE); + mCurDepthStencilState.depthMask = depthMask; +} + +// TODO(dianx) one bit for sampleAlphaToCoverage +void StateManager9::setSampleAlphaToCoverage(bool enabled) +{ + if (enabled) + { + FIXME("Sample alpha to coverage is unimplemented."); + } +} + +void StateManager9::setBlendColor(const gl::BlendState &blendState, const gl::ColorF &blendColor) +{ + if (!blendState.blend) + return; + + if (blendState.sourceBlendRGB != GL_CONSTANT_ALPHA && + blendState.sourceBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA && + blendState.destBlendRGB != GL_CONSTANT_ALPHA && + blendState.destBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA) + { + mRenderer9->getDevice()->SetRenderState(D3DRS_BLENDFACTOR, + gl_d3d9::ConvertColor(blendColor)); + } + else + { + mRenderer9->getDevice()->SetRenderState( + D3DRS_BLENDFACTOR, + D3DCOLOR_RGBA(gl::unorm<8>(blendColor.alpha), gl::unorm<8>(blendColor.alpha), + gl::unorm<8>(blendColor.alpha), gl::unorm<8>(blendColor.alpha))); + } + mCurBlendColor = blendColor; +} + +void StateManager9::setBlendFuncsEquations(const gl::BlendState &blendState) +{ + if (!blendState.blend) + return; + + IDirect3DDevice9 *device = mRenderer9->getDevice(); + + device->SetRenderState(D3DRS_SRCBLEND, gl_d3d9::ConvertBlendFunc(blendState.sourceBlendRGB)); + device->SetRenderState(D3DRS_DESTBLEND, gl_d3d9::ConvertBlendFunc(blendState.destBlendRGB)); + device->SetRenderState(D3DRS_BLENDOP, gl_d3d9::ConvertBlendOp(blendState.blendEquationRGB)); + + if (blendState.sourceBlendRGB != blendState.sourceBlendAlpha || + blendState.destBlendRGB != blendState.destBlendAlpha || + blendState.blendEquationRGB != blendState.blendEquationAlpha) + { + device->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE); + + device->SetRenderState(D3DRS_SRCBLENDALPHA, + gl_d3d9::ConvertBlendFunc(blendState.sourceBlendAlpha)); + device->SetRenderState(D3DRS_DESTBLENDALPHA, + gl_d3d9::ConvertBlendFunc(blendState.destBlendAlpha)); + device->SetRenderState(D3DRS_BLENDOPALPHA, + gl_d3d9::ConvertBlendOp(blendState.blendEquationAlpha)); + } + else + { + device->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, FALSE); + } + + mCurBlendState.sourceBlendRGB = blendState.sourceBlendRGB; + mCurBlendState.destBlendRGB = blendState.destBlendRGB; + mCurBlendState.blendEquationRGB = blendState.blendEquationRGB; + mCurBlendState.blendEquationAlpha = blendState.blendEquationAlpha; +} + +void StateManager9::setBlendEnabled(bool enabled) +{ + mRenderer9->getDevice()->SetRenderState(D3DRS_ALPHABLENDENABLE, enabled ? TRUE : FALSE); + mCurBlendState.blend = enabled; +} + +void StateManager9::setDither(bool dither) +{ + mRenderer9->getDevice()->SetRenderState(D3DRS_DITHERENABLE, dither ? TRUE : FALSE); + mCurBlendState.dither = dither; +} + +// TODO(dianx) one bit for color mask +void StateManager9::setColorMask(const gl::Framebuffer *framebuffer, + bool red, + bool blue, + bool green, + bool alpha) +{ + // Set the color mask + bool zeroColorMaskAllowed = mRenderer9->getVendorId() != VENDOR_ID_AMD; + // Apparently some ATI cards have a bug where a draw with a zero color + // write mask can cause later draws to have incorrect results. Instead, + // set a nonzero color write mask but modify the blend state so that no + // drawing is done. + // http://code.google.com/p/angleproject/issues/detail?id=169 + + const gl::FramebufferAttachment *attachment = framebuffer->getFirstColorbuffer(); + GLenum internalFormat = attachment ? attachment->getInternalFormat() : GL_NONE; + + const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat); + + DWORD colorMask = gl_d3d9::ConvertColorMask( + formatInfo.redBits > 0 && red, formatInfo.greenBits > 0 && green, + formatInfo.blueBits > 0 && blue, formatInfo.alphaBits > 0 && alpha); + + if (colorMask == 0 && !zeroColorMaskAllowed) + { + IDirect3DDevice9 *device = mRenderer9->getDevice(); + // Enable green channel, but set blending so nothing will be drawn. + device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_GREEN); + device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + + device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO); + device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); + } + else + { + mRenderer9->getDevice()->SetRenderState(D3DRS_COLORWRITEENABLE, colorMask); + } + + mCurBlendState.colorMaskRed = red; + mCurBlendState.colorMaskGreen = green; + mCurBlendState.colorMaskBlue = blue; + mCurBlendState.colorMaskAlpha = alpha; +} + +void StateManager9::setSampleMask(unsigned int sampleMask) +{ + IDirect3DDevice9 *device = mRenderer9->getDevice(); + // Set the multisample mask + device->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, TRUE); + device->SetRenderState(D3DRS_MULTISAMPLEMASK, static_cast(sampleMask)); + + mCurSampleMask = sampleMask; +} + +void StateManager9::setCullMode(bool cullFace, GLenum cullMode, GLenum frontFace) +{ + if (cullFace) + { + mRenderer9->getDevice()->SetRenderState(D3DRS_CULLMODE, + gl_d3d9::ConvertCullMode(cullMode, frontFace)); + } + else + { + mRenderer9->getDevice()->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + } + + mCurRasterState.cullFace = cullFace; + mCurRasterState.cullMode = cullMode; + mCurRasterState.frontFace = frontFace; +} + +void StateManager9::setDepthBias(bool polygonOffsetFill, + GLfloat polygonOffsetFactor, + GLfloat polygonOffsetUnits) +{ + if (polygonOffsetFill) + { + if (mCurDepthSize > 0) + { + IDirect3DDevice9 *device = mRenderer9->getDevice(); + device->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, *(DWORD *)&polygonOffsetFactor); + + float depthBias = ldexp(polygonOffsetUnits, -static_cast(mCurDepthSize)); + device->SetRenderState(D3DRS_DEPTHBIAS, *(DWORD *)&depthBias); + } + } + else + { + IDirect3DDevice9 *device = mRenderer9->getDevice(); + device->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, 0); + device->SetRenderState(D3DRS_DEPTHBIAS, 0); + } + + mCurRasterState.polygonOffsetFill = polygonOffsetFill; + mCurRasterState.polygonOffsetFactor = polygonOffsetFactor; + mCurRasterState.polygonOffsetUnits = polygonOffsetUnits; +} + +void StateManager9::updateDepthSizeIfChanged(bool depthStencilInitialized, unsigned int depthSize) +{ + if (!depthStencilInitialized || depthSize != mCurDepthSize) + { + mCurDepthSize = depthSize; + forceSetRasterState(); + } +} +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/StateManager9.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/StateManager9.h new file mode 100644 index 0000000000..d8c1eb9812 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/StateManager9.h @@ -0,0 +1,206 @@ +// +// Copyright (c) 2015 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. +// + +// StateManager9.h: Defines a class for caching D3D9 state + +#ifndef LIBANGLE_RENDERER_D3D9_STATEMANAGER9_H_ +#define LIBANGLE_RENDERER_D3D9_STATEMANAGER9_H_ + +#include "libANGLE/angletypes.h" +#include "libANGLE/Data.h" +#include "libANGLE/State.h" +#include "libANGLE/renderer/d3d/RendererD3D.h" + +namespace rx +{ + +class Renderer9; + +struct dx_VertexConstants9 +{ + float depthRange[4]; + float viewAdjust[4]; + float viewCoords[4]; +}; + +struct dx_PixelConstants9 +{ + float depthRange[4]; + float viewCoords[4]; + float depthFront[4]; +}; + +class StateManager9 final : angle::NonCopyable +{ + public: + StateManager9(Renderer9 *renderer9); + ~StateManager9(); + + void syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits); + + gl::Error setBlendDepthRasterStates(const gl::State &glState, unsigned int sampleMask); + void setScissorState(const gl::Rectangle &scissor, bool enabled); + void setViewportState(const gl::Caps *caps, + const gl::Rectangle &viewport, + float zNear, + float zFar, + GLenum drawMode, + GLenum frontFace, + bool ignoreViewport); + + void setShaderConstants(); + + void forceSetBlendState(); + void forceSetRasterState(); + void forceSetDepthStencilState(); + void forceSetScissorState(); + void forceSetViewportState(); + void forceSetDXUniformsState(); + + void updateDepthSizeIfChanged(bool depthStencilInitialized, unsigned int depthSize); + void updateStencilSizeIfChanged(bool depthStencilInitialized, unsigned int stencilSize); + + void setRenderTargetBounds(size_t width, size_t height); + + int getRenderTargetWidth() const { return mRenderTargetBounds.width; } + int getRenderTargetHeight() const { return mRenderTargetBounds.height; } + + void resetDirtyBits() { mDirtyBits.reset(); } + + private: + // Blend state functions + void setBlendEnabled(bool enabled); + void setBlendColor(const gl::BlendState &blendState, const gl::ColorF &blendColor); + void setBlendFuncsEquations(const gl::BlendState &blendState); + void setColorMask(const gl::Framebuffer *framebuffer, + bool red, + bool blue, + bool green, + bool alpha); + void setSampleAlphaToCoverage(bool enabled); + void setDither(bool dither); + void setSampleMask(unsigned int sampleMask); + + // Current raster state functions + void setCullMode(bool cullFace, GLenum cullMode, GLenum frontFace); + void setDepthBias(bool polygonOffsetFill, + GLfloat polygonOffsetFactor, + GLfloat polygonOffsetUnits); + + // Depth stencil state functions + void setStencilOpsFront(GLenum stencilFail, + GLenum stencilPassDepthFail, + GLenum stencilPassDepthPass, + bool frontFaceCCW); + void setStencilOpsBack(GLenum stencilBackFail, + GLenum stencilBackPassDepthFail, + GLenum stencilBackPassDepthPass, + bool frontFaceCCW); + void setStencilBackWriteMask(GLuint stencilBackWriteMask, bool frontFaceCCW); + void setDepthFunc(bool depthTest, GLenum depthFunc); + void setStencilTestEnabled(bool enabled); + void setDepthMask(bool depthMask); + void setStencilFuncsFront(GLenum stencilFunc, + GLuint stencilMask, + GLint stencilRef, + bool frontFaceCCW, + unsigned int maxStencil); + void setStencilFuncsBack(GLenum stencilBackFunc, + GLuint stencilBackMask, + GLint stencilBackRef, + bool frontFaceCCW, + unsigned int maxStencil); + void setStencilWriteMask(GLuint stencilWriteMask, bool frontFaceCCW); + + void setScissorEnabled(bool scissorEnabled); + void setScissorRect(const gl::Rectangle &scissor, bool enabled); + + enum DirtyBitType + { + // Blend dirty bits + DIRTY_BIT_BLEND_ENABLED, + DIRTY_BIT_BLEND_COLOR, + DIRTY_BIT_BLEND_FUNCS_EQUATIONS, + DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE, + DIRTY_BIT_COLOR_MASK, + DIRTY_BIT_DITHER, + DIRTY_BIT_SAMPLE_MASK, + + // Rasterizer dirty bits + DIRTY_BIT_CULL_MODE, + DIRTY_BIT_DEPTH_BIAS, + + // Depth stencil dirty bits + DIRTY_BIT_STENCIL_DEPTH_MASK, + DIRTY_BIT_STENCIL_DEPTH_FUNC, + DIRTY_BIT_STENCIL_TEST_ENABLED, + DIRTY_BIT_STENCIL_FUNCS_FRONT, + DIRTY_BIT_STENCIL_FUNCS_BACK, + DIRTY_BIT_STENCIL_WRITEMASK_FRONT, + DIRTY_BIT_STENCIL_WRITEMASK_BACK, + DIRTY_BIT_STENCIL_OPS_FRONT, + DIRTY_BIT_STENCIL_OPS_BACK, + + // Scissor dirty bits + DIRTY_BIT_SCISSOR_ENABLED, + DIRTY_BIT_SCISSOR_RECT, + + // Viewport dirty bits + DIRTY_BIT_VIEWPORT, + + DIRTY_BIT_MAX + }; + + typedef std::bitset DirtyBits; + + // Currently applied blend state + gl::BlendState mCurBlendState; + gl::ColorF mCurBlendColor; + unsigned int mCurSampleMask; + DirtyBits mBlendStateDirtyBits; + + // Currently applied raster state + gl::RasterizerState mCurRasterState; + unsigned int mCurDepthSize; + DirtyBits mRasterizerStateDirtyBits; + + // Currently applied depth stencil state + gl::DepthStencilState mCurDepthStencilState; + int mCurStencilRef; + int mCurStencilBackRef; + bool mCurFrontFaceCCW; + unsigned int mCurStencilSize; + DirtyBits mDepthStencilStateDirtyBits; + + // Currently applied scissor states + gl::Rectangle mCurScissorRect; + bool mCurScissorEnabled; + gl::Extents mRenderTargetBounds; + DirtyBits mScissorStateDirtyBits; + + // Currently applied viewport states + bool mForceSetViewport; + gl::Rectangle mCurViewport; + float mCurNear; + float mCurFar; + float mCurDepthFront; + bool mCurIgnoreViewport; + + dx_VertexConstants9 mVertexConstants; + dx_PixelConstants9 mPixelConstants; + bool mDxUniformsDirty; + + // FIXME: Unsupported by D3D9 + static const D3DRENDERSTATETYPE D3DRS_CCW_STENCILREF = D3DRS_STENCILREF; + static const D3DRENDERSTATETYPE D3DRS_CCW_STENCILMASK = D3DRS_STENCILMASK; + static const D3DRENDERSTATETYPE D3DRS_CCW_STENCILWRITEMASK = D3DRS_STENCILWRITEMASK; + + Renderer9 *mRenderer9; + DirtyBits mDirtyBits; +}; + +} // namespace rx +#endif // LIBANGLE_RENDERER_D3D9_STATEMANAGER9_H_ diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/SwapChain9.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/SwapChain9.cpp index 5ce76f8ae9..be6a9c424c 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/SwapChain9.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/SwapChain9.cpp @@ -15,21 +15,26 @@ namespace rx { -SwapChain9::SwapChain9(Renderer9 *renderer, NativeWindow nativeWindow, HANDLE shareHandle, - GLenum backBufferFormat, GLenum depthBufferFormat) +SwapChain9::SwapChain9(Renderer9 *renderer, + NativeWindow nativeWindow, + HANDLE shareHandle, + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation) : SwapChainD3D(nativeWindow, shareHandle, backBufferFormat, depthBufferFormat), mRenderer(renderer), + mWidth(-1), + mHeight(-1), + mSwapInterval(-1), + mSwapChain(nullptr), + mBackBuffer(nullptr), + mRenderTarget(nullptr), + mDepthStencil(nullptr), + mOffscreenTexture(nullptr), mColorRenderTarget(this, false), mDepthStencilRenderTarget(this, true) { - mSwapChain = NULL; - mBackBuffer = NULL; - mDepthStencil = NULL; - mRenderTarget = NULL; - mOffscreenTexture = NULL; - mWidth = -1; - mHeight = -1; - mSwapInterval = -1; + ASSERT(orientation == 0); } SwapChain9::~SwapChain9() diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/SwapChain9.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/SwapChain9.h index 9c67f7165b..55a700c2d6 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/SwapChain9.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/SwapChain9.h @@ -20,8 +20,12 @@ class Renderer9; class SwapChain9 : public SwapChainD3D { public: - SwapChain9(Renderer9 *renderer, NativeWindow nativeWindow, HANDLE shareHandle, - GLenum backBufferFormat, GLenum depthBufferFormat); + SwapChain9(Renderer9 *renderer, + NativeWindow nativeWindow, + HANDLE shareHandle, + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation); virtual ~SwapChain9(); EGLint resize(EGLint backbufferWidth, EGLint backbufferHeight); @@ -45,8 +49,8 @@ class SwapChain9 : public SwapChainD3D void release(); Renderer9 *mRenderer; - EGLint mHeight; EGLint mWidth; + EGLint mHeight; EGLint mSwapInterval; IDirect3DSwapChain9 *mSwapChain; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/formatutils9.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/formatutils9.cpp index 64dee8d686..471c90ff35 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/formatutils9.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/formatutils9.cpp @@ -475,16 +475,25 @@ template struct UseFallback { enum { type = T::fallback }; }; // and the D3DDECLTYPE member needed for the vertex declaration in declflag. template class PreferenceRule> struct Converter - : VertexDataConverter::type, - WidenRule >::type, size>, - ConversionRule >::type>, - DefaultVertexValues >::type>::type, normalized > > + : VertexDataConverter< + typename GLToCType::type, + WidenRule>::type, size>, + ConversionRule>::type>, + DefaultVertexValues>::type>::type, + normalized>> { private: - enum { d3dtype = PreferenceRule< VertexTypeMapping >::type }; - enum { d3dsize = WidenRule::finalWidth }; + enum + { + d3dtype = PreferenceRule>::type + }; + enum + { + d3dsize = WidenRule::finalWidth + }; public: enum { capflag = VertexTypeFlags::capflag }; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp index b931035ef6..8622dc4d13 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp @@ -558,6 +558,7 @@ void GenerateCaps(IDirect3D9 *d3d9, SafeRelease(eventQuery); extensions->timerQuery = false; // Unimplemented + extensions->disjointTimerQuery = false; extensions->robustness = true; extensions->blendMinMax = true; extensions->framebufferBlit = true; @@ -577,6 +578,7 @@ void GenerateCaps(IDirect3D9 *d3d9, extensions->unpackSubimage = true; extensions->packSubimage = true; extensions->vertexArrayObject = true; + extensions->noError = true; // D3D9 has no concept of separate masks and refs for front and back faces in the depth stencil // state. diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/vertexconversion.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/vertexconversion.h index 32eb376a78..aa05934bc8 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/vertexconversion.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/vertexconversion.h @@ -149,7 +149,10 @@ struct NormalizedDefaultValues // static const std::size_t finalSize: number of bytes per output vertex // static void convertArray(const void *in, std::size_t stride, std::size_t n, void *out): convert an array of vertices. Input may be strided, but output will be unstrided. -template > +template > struct VertexDataConverter { typedef typename Converter::OutputType OutputType; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.cpp b/gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.cpp index 97eb992995..26a3b32ce0 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.cpp @@ -15,6 +15,43 @@ namespace rx { namespace { +// Table 3.17.2 sorted according to table 3.17.3 +// clang-format off +static const int intensityModifierDefault[][4] = +{ + { 2, 8, -2, -8 }, + { 5, 17, -5, -17 }, + { 9, 29, -9, -29 }, + { 13, 42, -13, -42 }, + { 18, 60, -18, -60 }, + { 24, 80, -24, -80 }, + { 33, 106, -33, -106 }, + { 47, 183, -47, -183 }, +}; +// clang-format on + +// Table C.12, intensity modifier for non opaque punchthrough alpha +// clang-format off +static const int intensityModifierNonOpaque[][4] = +{ + { 0, 8, 0, -8 }, + { 0, 17, 0, -17 }, + { 0, 29, 0, -29 }, + { 0, 42, 0, -42 }, + { 0, 60, 0, -60 }, + { 0, 80, 0, -80 }, + { 0, 106, 0, -106 }, + { 0, 183, 0, -183 }, +}; +// clang-format on + +// Table C.7, mapping from pixel index values to modifier value orders +// clang-format off +static const int valueMappingTable[] = +{ + 2, 3, 1, 0 +}; +// clang-format on struct ETC2Block { @@ -92,6 +129,49 @@ struct ETC2Block } } + // Transcodes RGB block to BC1 + void transcodeAsBC1(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + const uint8_t alphaValues[4][4], + bool punchThroughAlpha) const + { + bool opaqueBit = u.idht.mode.idm.diffbit; + bool nonOpaquePunchThroughAlpha = punchThroughAlpha && !opaqueBit; + // Select mode + if (u.idht.mode.idm.diffbit || punchThroughAlpha) + { + const auto &block = u.idht.mode.idm.colors.diff; + int r = (block.R + block.dR); + int g = (block.G + block.dG); + int b = (block.B + block.dB); + if (r < 0 || r > 31) + { + transcodeTBlockToBC1(dest, x, y, w, h, alphaValues, nonOpaquePunchThroughAlpha); + } + else if (g < 0 || g > 31) + { + transcodeHBlockToBC1(dest, x, y, w, h, alphaValues, nonOpaquePunchThroughAlpha); + } + else if (b < 0 || b > 31) + { + transcodePlanarBlockToBC1(dest, x, y, w, h, alphaValues); + } + else + { + transcodeDifferentialBlockToBC1(dest, x, y, w, h, alphaValues, + nonOpaquePunchThroughAlpha); + } + } + else + { + transcodeIndividualBlockToBC1(dest, x, y, w, h, alphaValues, + nonOpaquePunchThroughAlpha); + } + } + private: union { @@ -329,48 +409,18 @@ struct ETC2Block const uint8_t alphaValues[4][4], bool nonOpaquePunchThroughAlpha) const { - // Table 3.17.2 sorted according to table 3.17.3 - // clang-format off - static const int intensityModifierDefault[8][4] = - { - { 2, 8, -2, -8 }, - { 5, 17, -5, -17 }, - { 9, 29, -9, -29 }, - { 13, 42, -13, -42 }, - { 18, 60, -18, -60 }, - { 24, 80, -24, -80 }, - { 33, 106, -33, -106 }, - { 47, 183, -47, -183 }, - }; - // clang-format on - - // Table C.12, intensity modifier for non opaque punchthrough alpha - // clang-format off - static const int intensityModifierNonOpaque[8][4] = - { - { 0, 8, 0, -8 }, - { 0, 17, 0, -17 }, - { 0, 29, 0, -29 }, - { 0, 42, 0, -42 }, - { 0, 60, 0, -60 }, - { 0, 80, 0, -80 }, - { 0, 106, 0, -106 }, - { 0, 183, 0, -183 }, - }; - // clang-format on - - const int(&intensityModifier)[8][4] = + const auto intensityModifier = nonOpaquePunchThroughAlpha ? intensityModifierNonOpaque : intensityModifierDefault; R8G8B8A8 subblockColors0[4]; R8G8B8A8 subblockColors1[4]; - for (size_t blockIdx = 0; blockIdx < 4; blockIdx++) + for (size_t modifierIdx = 0; modifierIdx < 4; modifierIdx++) { - const int i1 = intensityModifier[u.idht.mode.idm.cw1][blockIdx]; - subblockColors0[blockIdx] = createRGBA(r1 + i1, g1 + i1, b1 + i1); + const int i1 = intensityModifier[u.idht.mode.idm.cw1][modifierIdx]; + subblockColors0[modifierIdx] = createRGBA(r1 + i1, g1 + i1, b1 + i1); - const int i2 = intensityModifier[u.idht.mode.idm.cw2][blockIdx]; - subblockColors1[blockIdx] = createRGBA(r2 + i2, g2 + i2, b2 + i2); + const int i2 = intensityModifier[u.idht.mode.idm.cw2][modifierIdx]; + subblockColors1[modifierIdx] = createRGBA(r2 + i2, g2 + i2, b2 + i2); } if (u.idht.mode.idm.flipbit) @@ -583,6 +633,392 @@ struct ETC2Block } } + uint16_t RGB8ToRGB565(const R8G8B8A8 &rgba) const + { + return (static_cast(rgba.R >> 3) << 11) | + (static_cast(rgba.G >> 2) << 5) | + (static_cast(rgba.B >> 3) << 0); + } + + uint32_t matchBC1Bits(const R8G8B8A8 *rgba, + const R8G8B8A8 &minColor, + const R8G8B8A8 &maxColor, + bool opaque) const + { + // Project each pixel on the (maxColor, minColor) line to decide which + // BC1 code to assign to it. + + uint8_t decodedColors[2][3] = {{maxColor.R, maxColor.G, maxColor.B}, + {minColor.R, minColor.G, minColor.B}}; + + int direction[3]; + for (int ch = 0; ch < 3; ch++) + { + direction[ch] = decodedColors[0][ch] - decodedColors[1][ch]; + } + + int stops[2]; + for (int i = 0; i < 2; i++) + { + stops[i] = decodedColors[i][0] * direction[0] + decodedColors[i][1] * direction[1] + + decodedColors[i][2] * direction[2]; + } + + uint32_t bits = 0; + if (opaque) + { + for (int i = 15; i >= 0; i--) + { + // In opaque mode, the code is from 0 to 3. + + bits <<= 2; + const int dot = + rgba[i].R * direction[0] + rgba[i].G * direction[1] + rgba[i].B * direction[2]; + const int factor = gl::clamp( + static_cast( + (static_cast(dot - stops[1]) / (stops[0] - stops[1])) * 3 + 0.5f), + 0, 3); + switch (factor) + { + case 0: + bits |= 1; + break; + case 1: + bits |= 3; + break; + case 2: + bits |= 2; + break; + case 3: + default: + bits |= 0; + break; + } + } + } + else + { + for (int i = 15; i >= 0; i--) + { + // In non-opaque mode, 3 is for tranparent pixels. + + bits <<= 2; + if (0 == rgba[i].A) + { + bits |= 3; + } + else + { + const int dot = rgba[i].R * direction[0] + rgba[i].G * direction[1] + + rgba[i].B * direction[2]; + const int factor = gl::clamp( + static_cast( + (static_cast(dot - stops[1]) / (stops[0] - stops[1])) * 2 + + 0.5f), + 0, 2); + switch (factor) + { + case 0: + bits |= 0; + break; + case 1: + bits |= 2; + break; + case 2: + default: + bits |= 1; + break; + } + } + } + } + + return bits; + } + + void packBC1(void *bc1, + const R8G8B8A8 *rgba, + R8G8B8A8 &minColor, + R8G8B8A8 &maxColor, + bool opaque) const + { + uint32_t bits; + uint16_t max16 = RGB8ToRGB565(maxColor); + uint16_t min16 = RGB8ToRGB565(minColor); + if (max16 != min16) + { + // Find the best BC1 code for each pixel + bits = matchBC1Bits(rgba, minColor, maxColor, opaque); + } + else + { + // Same colors, BC1 index 0 is the color in both opaque and transparent mode + bits = 0; + // BC1 index 3 is transparent + if (!opaque) + { + for (int i = 0; i < 16; i++) + { + if (0 == rgba[i].A) + { + bits |= (3 << (i * 2)); + } + } + } + } + + if (max16 < min16) + { + std::swap(max16, min16); + + uint32_t xorMask = 0; + if (opaque) + { + // In opaque mode switching the two colors is doing the + // following code swaps: 0 <-> 1 and 2 <-> 3. This is + // equivalent to flipping the first bit of each code + // (5 = 0b0101) + xorMask = 0x55555555; + } + else + { + // In transparent mode switching the colors is doing the + // following code swap: 0 <-> 1. 0xA selects the second bit of + // each code, bits >> 1 selects the first bit of the code when + // the seconds bit is set (case 2 and 3). We invert all the + // non-selected bits, that is the first bit when the code is + // 0 or 1. + xorMask = ~((bits >> 1) | 0xAAAAAAAA); + } + bits ^= xorMask; + } + + struct BC1Block + { + uint16_t color0; + uint16_t color1; + uint32_t bits; + }; + + // Encode the opaqueness in the order of the two BC1 colors + BC1Block *dest = reinterpret_cast(bc1); + if (opaque) + { + dest->color0 = max16; + dest->color1 = min16; + } + else + { + dest->color0 = min16; + dest->color1 = max16; + } + dest->bits = bits; + } + + void transcodeIndividualBlockToBC1(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + const uint8_t alphaValues[4][4], + bool nonOpaquePunchThroughAlpha) const + { + const auto &block = u.idht.mode.idm.colors.indiv; + int r1 = extend_4to8bits(block.R1); + int g1 = extend_4to8bits(block.G1); + int b1 = extend_4to8bits(block.B1); + int r2 = extend_4to8bits(block.R2); + int g2 = extend_4to8bits(block.G2); + int b2 = extend_4to8bits(block.B2); + transcodeIndividualOrDifferentialBlockToBC1(dest, x, y, w, h, r1, g1, b1, r2, g2, b2, + alphaValues, nonOpaquePunchThroughAlpha); + } + + void transcodeDifferentialBlockToBC1(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + const uint8_t alphaValues[4][4], + bool nonOpaquePunchThroughAlpha) const + { + const auto &block = u.idht.mode.idm.colors.diff; + int b1 = extend_5to8bits(block.B); + int g1 = extend_5to8bits(block.G); + int r1 = extend_5to8bits(block.R); + int r2 = extend_5to8bits(block.R + block.dR); + int g2 = extend_5to8bits(block.G + block.dG); + int b2 = extend_5to8bits(block.B + block.dB); + transcodeIndividualOrDifferentialBlockToBC1(dest, x, y, w, h, r1, g1, b1, r2, g2, b2, + alphaValues, nonOpaquePunchThroughAlpha); + } + + void decodeSubblock(R8G8B8A8 *rgbaBlock, + size_t pixelRange[2][2], + size_t x, + size_t y, + size_t w, + size_t h, + const uint8_t alphaValues[4][4], + bool flipbit, + size_t subblockIdx, + const R8G8B8A8 subblockColors[2][4]) const + { + size_t dxBegin = 0; + size_t dxEnd = 4; + size_t dyBegin = subblockIdx * 2; + size_t dyEnd = dyBegin + 2; + if (!flipbit) + { + std::swap(dxBegin, dyBegin); + std::swap(dxEnd, dyEnd); + } + + for (size_t j = dyBegin; j < dyEnd && (y + j) < h; j++) + { + R8G8B8A8 *row = &rgbaBlock[j * 4]; + for (size_t i = dxBegin; i < dxEnd && (x + i) < w; i++) + { + const size_t pixelIndex = getIndex(i, j); + if (valueMappingTable[pixelIndex] < valueMappingTable[pixelRange[subblockIdx][0]]) + { + pixelRange[subblockIdx][0] = pixelIndex; + } + if (valueMappingTable[pixelIndex] > valueMappingTable[pixelRange[subblockIdx][1]]) + { + pixelRange[subblockIdx][1] = pixelIndex; + } + + row[i] = subblockColors[subblockIdx][pixelIndex]; + row[i].A = alphaValues[j][i]; + } + } + } + + void transcodeIndividualOrDifferentialBlockToBC1(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + int r1, + int g1, + int b1, + int r2, + int g2, + int b2, + const uint8_t alphaValues[4][4], + bool nonOpaquePunchThroughAlpha) const + { + // A BC1 block has 2 endpoints, pixels is encoded as linear + // interpolations of them. A ETC1/ETC2 individual or differential block + // has 2 subblocks. Each subblock has one color and a modifier. We + // compute the max intensity and min intensity pixel values to use as + // our two BC1 endpoints and then map pixels to BC1 by projecting on the + // line between the two endpoints and choosing the right fraction. + // + // In the future, we have 2 potential improvements to this algorithm. + // 1. We don't actually need to decode ETC blocks to RGBs. Instead, + // the subblock colors and pixel indices alreay contains enough + // information for transcode. A direct mapping would be more + // efficient here. + // 2. Currently the BC1 endpoints come from the max and min intensity + // of ETC colors. A principal component analysis (PCA) on them might + // give us better quality results, with limited costs + + const auto intensityModifier = + nonOpaquePunchThroughAlpha ? intensityModifierNonOpaque : intensityModifierDefault; + + // Compute the colors that pixels can have in each subblock both for + // the decoding of the RGBA data and BC1 encoding + R8G8B8A8 subblockColors[2][4]; + for (size_t modifierIdx = 0; modifierIdx < 4; modifierIdx++) + { + const int i1 = intensityModifier[u.idht.mode.idm.cw1][modifierIdx]; + subblockColors[0][modifierIdx] = createRGBA(r1 + i1, g1 + i1, b1 + i1); + + const int i2 = intensityModifier[u.idht.mode.idm.cw2][modifierIdx]; + subblockColors[1][modifierIdx] = createRGBA(r2 + i2, g2 + i2, b2 + i2); + } + + // 1 and 3 are the argmax and argmin of valueMappingTable + size_t pixelRange[2][2] = {{1, 3}, {1, 3}}; + R8G8B8A8 rgbaBlock[16]; + // Decode the block in rgbaBlock and store the inverse valueTableMapping + // of {min(modifier index), max(modifier index)} + for (size_t blockIdx = 0; blockIdx < 2; blockIdx++) + { + decodeSubblock(rgbaBlock, pixelRange, x, y, w, h, alphaValues, u.idht.mode.idm.flipbit, + blockIdx, subblockColors); + } + if (nonOpaquePunchThroughAlpha) + { + decodePunchThroughAlphaBlock(reinterpret_cast(rgbaBlock), x, y, w, h, + sizeof(R8G8B8A8) * 4); + } + + // Get the "min" and "max" pixel colors that have been used. + R8G8B8A8 minColor; + const R8G8B8A8 &minColor0 = subblockColors[0][pixelRange[0][0]]; + const R8G8B8A8 &minColor1 = subblockColors[1][pixelRange[1][0]]; + if (minColor0.R + minColor0.G + minColor0.B < minColor1.R + minColor1.G + minColor1.B) + { + minColor = minColor0; + } + else + { + minColor = minColor1; + } + + R8G8B8A8 maxColor; + const R8G8B8A8 &maxColor0 = subblockColors[0][pixelRange[0][1]]; + const R8G8B8A8 &maxColor1 = subblockColors[1][pixelRange[1][1]]; + if (maxColor0.R + maxColor0.G + maxColor0.B < maxColor1.R + maxColor1.G + maxColor1.B) + { + maxColor = maxColor1; + } + else + { + maxColor = maxColor0; + } + + packBC1(dest, rgbaBlock, minColor, maxColor, !nonOpaquePunchThroughAlpha); + } + + void transcodeTBlockToBC1(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + const uint8_t alphaValues[4][4], + bool nonOpaquePunchThroughAlpha) const + { + // TODO (mgong): Will be implemented soon + UNIMPLEMENTED(); + } + + void transcodeHBlockToBC1(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + const uint8_t alphaValues[4][4], + bool nonOpaquePunchThroughAlpha) const + { + // TODO (mgong): Will be implemented soon + UNIMPLEMENTED(); + } + + void transcodePlanarBlockToBC1(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + const uint8_t alphaValues[4][4]) const + { + // TODO (mgong): Will be implemented soon + UNIMPLEMENTED(); + } + // Single channel utility functions int getSingleChannel(size_t x, size_t y, bool isSigned) const { @@ -756,6 +1192,38 @@ void LoadETC2RGB8ToRGBA8(size_t width, } } +void LoadETC2RGB8ToBC1(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch, + bool punchthroughAlpha) +{ + for (size_t z = 0; z < depth; z++) + { + for (size_t y = 0; y < height; y += 4) + { + const ETC2Block *sourceRow = + OffsetDataPointer(input, y / 4, z, inputRowPitch, inputDepthPitch); + uint8_t *destRow = + OffsetDataPointer(output, y / 4, z, outputRowPitch, outputDepthPitch); + + for (size_t x = 0; x < width; x += 4) + { + const ETC2Block *sourceBlock = sourceRow + (x / 4); + uint8_t *destPixels = destRow + (x * 2); + + sourceBlock->transcodeAsBC1(destPixels, x, y, width, height, DefaultETCAlphaValues, + punchthroughAlpha); + } + } + } +} + void LoadETC2RGBA8ToRGBA8(size_t width, size_t height, size_t depth, @@ -810,6 +1278,20 @@ void LoadETC1RGB8ToRGBA8(size_t width, outputRowPitch, outputDepthPitch, false); } +void LoadETC1RGB8ToBC1(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{ + LoadETC2RGB8ToBC1(width, height, depth, input, inputRowPitch, inputDepthPitch, output, + outputRowPitch, outputDepthPitch, false); +} + void LoadEACR11ToR8(size_t width, size_t height, size_t depth, diff --git a/gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.h b/gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.h index 2ada743a24..dc64e0461b 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.h @@ -26,6 +26,16 @@ void LoadETC1RGB8ToRGBA8(size_t width, size_t outputRowPitch, size_t outputDepthPitch); +void LoadETC1RGB8ToBC1(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch); + void LoadEACR11ToR8(size_t width, size_t height, size_t depth, diff --git a/gfx/angle/src/libANGLE/renderer/gl/DisplayGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/DisplayGL.cpp index 8c809d6ce8..deecb03c29 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/DisplayGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/DisplayGL.cpp @@ -55,16 +55,12 @@ ImageImpl *DisplayGL::createImage(EGLenum target, return nullptr; } -egl::Error DisplayGL::createContext(const egl::Config *config, const gl::Context *shareContext, const egl::AttributeMap &attribs, gl::Context **outContext) +gl::Context *DisplayGL::createContext(const egl::Config *config, + const gl::Context *shareContext, + const egl::AttributeMap &attribs) { ASSERT(mRenderer != nullptr); - - EGLint clientVersion = attribs.get(EGL_CONTEXT_CLIENT_VERSION, 1); - bool notifyResets = (attribs.get(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT, EGL_NO_RESET_NOTIFICATION_EXT) == EGL_LOSE_CONTEXT_ON_RESET_EXT); - bool robustAccess = (attribs.get(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT, EGL_FALSE) == EGL_TRUE); - - *outContext = new gl::Context(config, clientVersion, shareContext, mRenderer, notifyResets, robustAccess); - return egl::Error(EGL_SUCCESS); + return new gl::Context(config, shareContext, mRenderer, attribs); } egl::Error DisplayGL::makeCurrent(egl::Surface *drawSurface, egl::Surface *readSurface, gl::Context *context) diff --git a/gfx/angle/src/libANGLE/renderer/gl/DisplayGL.h b/gfx/angle/src/libANGLE/renderer/gl/DisplayGL.h index 363d31a90e..b5b7228b0b 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/DisplayGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/DisplayGL.h @@ -30,8 +30,9 @@ class DisplayGL : public DisplayImpl egl::ImageSibling *buffer, const egl::AttributeMap &attribs) override; - egl::Error createContext(const egl::Config *config, const gl::Context *shareContext, const egl::AttributeMap &attribs, - gl::Context **outContext) override; + gl::Context *createContext(const egl::Config *config, + const gl::Context *shareContext, + const egl::AttributeMap &attribs) override; egl::Error makeCurrent(egl::Surface *drawSurface, egl::Surface *readSurface, gl::Context *context) override; diff --git a/gfx/angle/src/libANGLE/renderer/gl/FramebufferGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/FramebufferGL.cpp index 92e08cc06f..566de45c62 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/FramebufferGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/FramebufferGL.cpp @@ -8,6 +8,7 @@ #include "libANGLE/renderer/gl/FramebufferGL.h" +#include "common/BitSetIterator.h" #include "common/debug.h" #include "libANGLE/Data.h" #include "libANGLE/State.h" @@ -19,11 +20,14 @@ #include "libANGLE/renderer/gl/StateManagerGL.h" #include "libANGLE/renderer/gl/TextureGL.h" #include "libANGLE/renderer/gl/WorkaroundsGL.h" +#include "platform/Platform.h" + +using namespace gl; namespace rx { -FramebufferGL::FramebufferGL(const gl::Framebuffer::Data &data, +FramebufferGL::FramebufferGL(const Framebuffer::Data &data, const FunctionsGL *functions, StateManagerGL *stateManager, const WorkaroundsGL &workarounds, @@ -42,7 +46,7 @@ FramebufferGL::FramebufferGL(const gl::Framebuffer::Data &data, } FramebufferGL::FramebufferGL(GLuint id, - const gl::Framebuffer::Data &data, + const Framebuffer::Data &data, const FunctionsGL *functions, const WorkaroundsGL &workarounds, StateManagerGL *stateManager) @@ -61,14 +65,15 @@ FramebufferGL::~FramebufferGL() mFramebufferID = 0; } -static void BindFramebufferAttachment(const FunctionsGL *functions, GLenum attachmentPoint, - const gl::FramebufferAttachment *attachment) +static void BindFramebufferAttachment(const FunctionsGL *functions, + GLenum attachmentPoint, + const FramebufferAttachment *attachment) { if (attachment) { if (attachment->type() == GL_TEXTURE) { - const gl::Texture *texture = attachment->getTexture(); + const Texture *texture = attachment->getTexture(); const TextureGL *textureGL = GetImplAs(texture); if (texture->getTarget() == GL_TEXTURE_2D) @@ -93,7 +98,7 @@ static void BindFramebufferAttachment(const FunctionsGL *functions, GLenum attac } else if (attachment->type() == GL_RENDERBUFFER) { - const gl::Renderbuffer *renderbuffer = attachment->getRenderbuffer(); + const Renderbuffer *renderbuffer = attachment->getRenderbuffer(); const RenderbufferGL *renderbufferGL = GetImplAs(renderbuffer); functions->framebufferRenderbuffer(GL_FRAMEBUFFER, attachmentPoint, GL_RENDERBUFFER, @@ -111,74 +116,13 @@ static void BindFramebufferAttachment(const FunctionsGL *functions, GLenum attac } } -void FramebufferGL::onUpdateColorAttachment(size_t index) -{ - if (!mIsDefault) - { - mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); - BindFramebufferAttachment(mFunctions, GL_COLOR_ATTACHMENT0 + static_cast(index), - mData.getColorAttachment(static_cast(index))); - } -} - -void FramebufferGL::onUpdateDepthAttachment() -{ - if (!mIsDefault) - { - mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); - BindFramebufferAttachment(mFunctions, - GL_DEPTH_ATTACHMENT, - mData.getDepthAttachment()); - } -} - -void FramebufferGL::onUpdateStencilAttachment() -{ - if (!mIsDefault) - { - mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); - BindFramebufferAttachment(mFunctions, - GL_STENCIL_ATTACHMENT, - mData.getStencilAttachment()); - } -} - -void FramebufferGL::onUpdateDepthStencilAttachment() -{ - if (!mIsDefault) - { - mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); - BindFramebufferAttachment(mFunctions, - GL_DEPTH_STENCIL_ATTACHMENT, - mData.getDepthStencilAttachment()); - } -} - -void FramebufferGL::setDrawBuffers(size_t count, const GLenum *buffers) -{ - if (!mIsDefault) - { - mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); - mFunctions->drawBuffers(static_cast(count), buffers); - } -} - -void FramebufferGL::setReadBuffer(GLenum buffer) -{ - if (!mIsDefault) - { - mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); - mFunctions->readBuffer(buffer); - } -} - -gl::Error FramebufferGL::discard(size_t count, const GLenum *attachments) +Error FramebufferGL::discard(size_t count, const GLenum *attachments) { UNIMPLEMENTED(); - return gl::Error(GL_INVALID_OPERATION); + return Error(GL_INVALID_OPERATION); } -gl::Error FramebufferGL::invalidate(size_t count, const GLenum *attachments) +Error FramebufferGL::invalidate(size_t count, const GLenum *attachments) { // Since this function is just a hint and not available until OpenGL 4.3, only call it if it is available. if (mFunctions->invalidateFramebuffer) @@ -187,10 +131,12 @@ gl::Error FramebufferGL::invalidate(size_t count, const GLenum *attachments) mFunctions->invalidateFramebuffer(GL_FRAMEBUFFER, static_cast(count), attachments); } - return gl::Error(GL_NO_ERROR); + return Error(GL_NO_ERROR); } -gl::Error FramebufferGL::invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area) +Error FramebufferGL::invalidateSub(size_t count, + const GLenum *attachments, + const gl::Rectangle &area) { // Since this function is just a hint and not available until OpenGL 4.3, only call it if it is available. if (mFunctions->invalidateSubFramebuffer) @@ -200,102 +146,174 @@ gl::Error FramebufferGL::invalidateSub(size_t count, const GLenum *attachments, attachments, area.x, area.y, area.width, area.height); } - return gl::Error(GL_NO_ERROR); + return Error(GL_NO_ERROR); } -gl::Error FramebufferGL::clear(const gl::Data &data, GLbitfield mask) +Error FramebufferGL::clear(const Data &data, GLbitfield mask) { syncClearState(mask); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mFunctions->clear(mask); - return gl::Error(GL_NO_ERROR); + return Error(GL_NO_ERROR); } -gl::Error FramebufferGL::clearBufferfv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLfloat *values) +Error FramebufferGL::clearBufferfv(const Data &data, + GLenum buffer, + GLint drawbuffer, + const GLfloat *values) { syncClearBufferState(buffer, drawbuffer); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mFunctions->clearBufferfv(buffer, drawbuffer, values); - return gl::Error(GL_NO_ERROR); + return Error(GL_NO_ERROR); } -gl::Error FramebufferGL::clearBufferuiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLuint *values) +Error FramebufferGL::clearBufferuiv(const Data &data, + GLenum buffer, + GLint drawbuffer, + const GLuint *values) { syncClearBufferState(buffer, drawbuffer); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mFunctions->clearBufferuiv(buffer, drawbuffer, values); - return gl::Error(GL_NO_ERROR); + return Error(GL_NO_ERROR); } -gl::Error FramebufferGL::clearBufferiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLint *values) +Error FramebufferGL::clearBufferiv(const Data &data, + GLenum buffer, + GLint drawbuffer, + const GLint *values) { syncClearBufferState(buffer, drawbuffer); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mFunctions->clearBufferiv(buffer, drawbuffer, values); - return gl::Error(GL_NO_ERROR); + return Error(GL_NO_ERROR); } -gl::Error FramebufferGL::clearBufferfi(const gl::State &state, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) +Error FramebufferGL::clearBufferfi(const Data &data, + GLenum buffer, + GLint drawbuffer, + GLfloat depth, + GLint stencil) { syncClearBufferState(buffer, drawbuffer); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mFunctions->clearBufferfi(buffer, drawbuffer, depth, stencil); - return gl::Error(GL_NO_ERROR); + return Error(GL_NO_ERROR); } GLenum FramebufferGL::getImplementationColorReadFormat() const { - const gl::FramebufferAttachment *readAttachment = getData().getReadAttachment(); + const FramebufferAttachment *readAttachment = getData().getReadAttachment(); GLenum internalFormat = readAttachment->getInternalFormat(); - const gl::InternalFormat &internalFormatInfo = gl::GetInternalFormatInfo(internalFormat); + const InternalFormat &internalFormatInfo = GetInternalFormatInfo(internalFormat); return internalFormatInfo.format; } GLenum FramebufferGL::getImplementationColorReadType() const { - const gl::FramebufferAttachment *readAttachment = getData().getReadAttachment(); + const FramebufferAttachment *readAttachment = getData().getReadAttachment(); GLenum internalFormat = readAttachment->getInternalFormat(); - const gl::InternalFormat &internalFormatInfo = gl::GetInternalFormatInfo(internalFormat); + const InternalFormat &internalFormatInfo = GetInternalFormatInfo(internalFormat); return internalFormatInfo.type; } -gl::Error FramebufferGL::readPixels(const gl::State &state, const gl::Rectangle &area, GLenum format, GLenum type, GLvoid *pixels) const +Error FramebufferGL::readPixels(const State &state, + const gl::Rectangle &area, + GLenum format, + GLenum type, + GLvoid *pixels) const { // TODO: don't sync the pixel pack state here once the dirty bits contain the pixel pack buffer // binding - const gl::PixelPackState &packState = state.getPackState(); + const PixelPackState &packState = state.getPackState(); mStateManager->setPixelPackState(packState); mStateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferID); mFunctions->readPixels(area.x, area.y, area.width, area.height, format, type, pixels); - return gl::Error(GL_NO_ERROR); + return Error(GL_NO_ERROR); } -gl::Error FramebufferGL::blit(const gl::State &state, const gl::Rectangle &sourceArea, const gl::Rectangle &destArea, - GLbitfield mask, GLenum filter, const gl::Framebuffer *sourceFramebuffer) +Error FramebufferGL::blit(const State &state, + const gl::Rectangle &sourceArea, + const gl::Rectangle &destArea, + GLbitfield mask, + GLenum filter, + const Framebuffer *sourceFramebuffer) { const FramebufferGL *sourceFramebufferGL = GetImplAs(sourceFramebuffer); mStateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebufferGL->getFramebufferID()); mStateManager->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferID); - mFunctions->blitFramebuffer(sourceArea.x, sourceArea.y, sourceArea.x + sourceArea.width, sourceArea.y + sourceArea.height, - destArea.x, destArea.y, destArea.x + destArea.width, destArea.y + destArea.height, - mask, filter); + mFunctions->blitFramebuffer(sourceArea.x, sourceArea.y, sourceArea.x1(), sourceArea.y1(), + destArea.x, destArea.y, destArea.x1(), destArea.y1(), mask, filter); - return gl::Error(GL_NO_ERROR); + return Error(GL_NO_ERROR); } -GLenum FramebufferGL::checkStatus() const +bool FramebufferGL::checkStatus() const { mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); - return mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER); + GLenum status = mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) + { + ANGLEPlatformCurrent()->logWarning("GL framebuffer returned incomplete."); + } + return (status == GL_FRAMEBUFFER_COMPLETE); +} + +void FramebufferGL::syncState(const Framebuffer::DirtyBits &dirtyBits) +{ + // Don't need to sync state for the default FBO. + if (mIsDefault) + { + return; + } + + mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); + + for (auto dirtyBit : angle::IterateBitSet(dirtyBits)) + { + switch (dirtyBit) + { + case Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT: + BindFramebufferAttachment(mFunctions, GL_DEPTH_ATTACHMENT, + mData.getDepthAttachment()); + break; + case Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT: + BindFramebufferAttachment(mFunctions, GL_STENCIL_ATTACHMENT, + mData.getStencilAttachment()); + break; + case Framebuffer::DIRTY_BIT_DRAW_BUFFERS: + { + const auto &drawBuffers = mData.getDrawBufferStates(); + mFunctions->drawBuffers(static_cast(drawBuffers.size()), + drawBuffers.data()); + break; + } + case Framebuffer::DIRTY_BIT_READ_BUFFER: + mFunctions->readBuffer(mData.getReadBufferState()); + break; + default: + { + ASSERT(Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0 && + dirtyBit < Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX); + size_t index = + static_cast(dirtyBit - Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0); + BindFramebufferAttachment(mFunctions, + static_cast(GL_COLOR_ATTACHMENT0 + index), + mData.getColorAttachment(index)); + break; + } + } + } } GLuint FramebufferGL::getFramebufferID() const @@ -351,7 +369,7 @@ void FramebufferGL::syncClearBufferState(GLenum buffer, GLint drawBuffer) const auto &drawbufferState = mData.getDrawBufferStates(); const auto &colorAttachments = mData.getColorAttachments(); - const gl::FramebufferAttachment *attachment = nullptr; + const FramebufferAttachment *attachment = nullptr; if (drawbufferState[drawBuffer] >= GL_COLOR_ATTACHMENT0 && drawbufferState[drawBuffer] < GL_COLOR_ATTACHMENT0 + colorAttachments.size()) { @@ -371,4 +389,4 @@ void FramebufferGL::syncClearBufferState(GLenum buffer, GLint drawBuffer) } } } -} +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/gl/FramebufferGL.h b/gfx/angle/src/libANGLE/renderer/gl/FramebufferGL.h index edc4fa7faf..224b10c7b9 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/FramebufferGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/FramebufferGL.h @@ -36,23 +36,28 @@ class FramebufferGL : public FramebufferImpl StateManagerGL *stateManager); ~FramebufferGL() override; - void onUpdateColorAttachment(size_t index) override; - void onUpdateDepthAttachment() override; - void onUpdateStencilAttachment() override; - void onUpdateDepthStencilAttachment() override; - - void setDrawBuffers(size_t count, const GLenum *buffers) override; - void setReadBuffer(GLenum buffer) override; - gl::Error discard(size_t count, const GLenum *attachments) override; gl::Error invalidate(size_t count, const GLenum *attachments) override; gl::Error invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area) override; gl::Error clear(const gl::Data &data, GLbitfield mask) override; - gl::Error clearBufferfv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLfloat *values) override; - gl::Error clearBufferuiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLuint *values) override; - gl::Error clearBufferiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLint *values) override; - gl::Error clearBufferfi(const gl::State &state, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) override; + gl::Error clearBufferfv(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + const GLfloat *values) override; + gl::Error clearBufferuiv(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + const GLuint *values) override; + gl::Error clearBufferiv(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + const GLint *values) override; + gl::Error clearBufferfi(const gl::Data &data, + GLenum buffer, + GLint drawbuffer, + GLfloat depth, + GLint stencil) override; GLenum getImplementationColorReadFormat() const override; GLenum getImplementationColorReadType() const override; @@ -61,7 +66,9 @@ class FramebufferGL : public FramebufferImpl gl::Error blit(const gl::State &state, const gl::Rectangle &sourceArea, const gl::Rectangle &destArea, GLbitfield mask, GLenum filter, const gl::Framebuffer *sourceFramebuffer) override; - GLenum checkStatus() const override; + bool checkStatus() const override; + + void syncState(const gl::Framebuffer::DirtyBits &dirtyBits) override; void syncDrawState() const; diff --git a/gfx/angle/src/libANGLE/renderer/gl/FunctionsGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/FunctionsGL.cpp index 37e13a4d3b..bd7d773f14 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/FunctionsGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/FunctionsGL.cpp @@ -760,7 +760,11 @@ FunctionsGL::FunctionsGL() vertexArrayBindingDivisor(nullptr), vertexArrayElementBuffer(nullptr), vertexArrayVertexBuffer(nullptr), - vertexArrayVertexBuffers(nullptr) + vertexArrayVertexBuffers(nullptr), + blendBarrier(nullptr), + primitiveBoundingBox(nullptr), + eglImageTargetRenderbufferStorageOES(nullptr), + eglImageTargetTexture2DOES(nullptr) { } @@ -776,7 +780,7 @@ void FunctionsGL::initialize() GetGLVersion(getString, &version, &standard); // Grab the GL extensions - if (isAtLeastGL(gl::Version(3, 0))) + if (isAtLeastGL(gl::Version(3, 0)) || isAtLeastGLES(gl::Version(3, 0))) { AssignGLEntryPoint(loadProcAddress("glGetStringi"), &getStringi); extensions = GetIndexedExtensions(getIntegerv, getStringi); @@ -787,6 +791,25 @@ void FunctionsGL::initialize() angle::SplitStringAlongWhitespace(std::string(exts), &extensions); } + // Load the entry points + switch (standard) + { + case STANDARD_GL_DESKTOP: + initializeProcsDesktopGL(); + break; + + case STANDARD_GL_ES: + initializeProcsGLES(); + break; + + default: + UNREACHABLE(); + break; + } +} + +void FunctionsGL::initializeProcsDesktopGL() +{ // Check the context profile profile = 0; if (isAtLeastGL(gl::Version(3, 2))) @@ -1684,6 +1707,471 @@ void FunctionsGL::initialize() // clang-format on } +void FunctionsGL::initializeProcsGLES() +{ + // No profiles in GLES + profile = 0; + + // clang-format off + + // GL_OES_texture_3D + AssignGLExtensionEntryPoint(extensions, "GL_OES_texture_3D", loadProcAddress("glTexImage3DOES"), &texImage3D); + AssignGLExtensionEntryPoint(extensions, "GL_OES_texture_3D", loadProcAddress("glTexSubImage3DOES"), &texSubImage3D); + AssignGLExtensionEntryPoint(extensions, "GL_OES_texture_3D", loadProcAddress("glCopyTexSubImage3DOES"), ©TexSubImage3D); + + // GL_NV_fence + AssignGLExtensionEntryPoint(extensions, "GL_NV_fence", loadProcAddress("glDeleteFencesNV"), &deleteFencesNV); + AssignGLExtensionEntryPoint(extensions, "GL_NV_fence", loadProcAddress("glGenFencesNV"), &genFencesNV); + AssignGLExtensionEntryPoint(extensions, "GL_NV_fence", loadProcAddress("glIsFenceNV"), &isFenceNV); + AssignGLExtensionEntryPoint(extensions, "GL_NV_fence", loadProcAddress("glTestFenceNV"), &testFenceNV); + AssignGLExtensionEntryPoint(extensions, "GL_NV_fence", loadProcAddress("glGetFenceivNV"), &getFenceivNV); + AssignGLExtensionEntryPoint(extensions, "GL_NV_fence", loadProcAddress("glFinishFenceNV"), &finishFenceNV); + AssignGLExtensionEntryPoint(extensions, "GL_NV_fence", loadProcAddress("glSetFenceNV"), &setFenceNV); + + // GL_EXT_texture_storage + AssignGLExtensionEntryPoint(extensions, "GL_EXT_texture_storage", loadProcAddress("glTexStorage2DEXT"), &texStorage2D); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_texture_storage GL_OES_texture3D", loadProcAddress("glTexStorage3DEXT"), &texStorage3D); + + // GL_OES_vertex_array_object + AssignGLExtensionEntryPoint(extensions, "GL_OES_vertex_array_object", loadProcAddress("glBindVertexArray"), &bindVertexArray); + AssignGLExtensionEntryPoint(extensions, "GL_OES_vertex_array_object", loadProcAddress("glDeleteVertexArrays"), &deleteVertexArrays); + AssignGLExtensionEntryPoint(extensions, "GL_OES_vertex_array_object", loadProcAddress("glGenVertexArrays"), &genVertexArrays); + AssignGLExtensionEntryPoint(extensions, "GL_OES_vertex_array_object", loadProcAddress("glIsVertexArray"), &isVertexArray); + + // GL_EXT_map_buffer_range + AssignGLExtensionEntryPoint(extensions, "GL_EXT_map_buffer_range", loadProcAddress("glMapBufferRangeEXT"), &mapBufferRange); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_map_buffer_range", loadProcAddress("glFlushMappedBufferRangeEXT"), &flushMappedBufferRange); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_map_buffer_range", loadProcAddress("glUnmapBufferOES"), &unmapBuffer); + + // GL_OES_mapbuffer + AssignGLExtensionEntryPoint(extensions, "GL_OES_mapbuffer", loadProcAddress("glMapBufferOES"), &mapBuffer); + AssignGLExtensionEntryPoint(extensions, "GL_OES_mapbuffer", loadProcAddress("glUnmapBufferOES"), &unmapBuffer); + + // GL_KHR_debug + AssignGLExtensionEntryPoint(extensions, "GL_KHR_debug", loadProcAddress("glDebugMessageControl"), &debugMessageControl); + AssignGLExtensionEntryPoint(extensions, "GL_KHR_debug", loadProcAddress("glDebugMessageInsert"), &debugMessageInsert); + AssignGLExtensionEntryPoint(extensions, "GL_KHR_debug", loadProcAddress("glDebugMessageCallback"), &debugMessageCallback); + AssignGLExtensionEntryPoint(extensions, "GL_KHR_debug", loadProcAddress("glGetDebugMessageLog"), &getDebugMessageLog); + AssignGLExtensionEntryPoint(extensions, "GL_KHR_debug", loadProcAddress("glGetPointerv"), &getPointerv); + AssignGLExtensionEntryPoint(extensions, "GL_KHR_debug", loadProcAddress("glPushDebugGroup"), &pushDebugGroup); + AssignGLExtensionEntryPoint(extensions, "GL_KHR_debug", loadProcAddress("glPopDebugGroup"), &popDebugGroup); + AssignGLExtensionEntryPoint(extensions, "GL_KHR_debug", loadProcAddress("glObjectLabel"), &objectLabel); + AssignGLExtensionEntryPoint(extensions, "GL_KHR_debug", loadProcAddress("glGetObjectLabel"), &getObjectLabel); + AssignGLExtensionEntryPoint(extensions, "GL_KHR_debug", loadProcAddress("glObjectPtrLabel"), &objectPtrLabel); + AssignGLExtensionEntryPoint(extensions, "GL_KHR_debug", loadProcAddress("glGetObjectPtrLabel"), &getObjectPtrLabel); + + // GL_EXT_draw_instanced + AssignGLExtensionEntryPoint(extensions, "GL_EXT_draw_instanced", loadProcAddress("glVertexAttribDivisorEXT"), &vertexAttribDivisor); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_draw_instanced", loadProcAddress("glDrawArraysInstancedEXT"), &drawArraysInstanced); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_draw_instanced", loadProcAddress("glDrawElementsInstancedEXT"), &drawElementsInstanced); + + // GL_EXT_occlusion_query_boolean + AssignGLExtensionEntryPoint(extensions, "GL_EXT_occlusion_query_boolean", loadProcAddress("glGenQueriesEXT"), &genQueries); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_occlusion_query_boolean", loadProcAddress("glDeleteQueriesEXT"), &deleteQueries); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_occlusion_query_boolean", loadProcAddress("glIsQueryEXT"), &isQuery); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_occlusion_query_boolean", loadProcAddress("glBeginQueryEXT"), &beginQuery); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_occlusion_query_boolean", loadProcAddress("glEndQueryEXT"), &endQuery); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_occlusion_query_boolean", loadProcAddress("glGetQueryivEXT"), &getQueryiv); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_occlusion_query_boolean", loadProcAddress("glGetQueryObjectuivEXT"), &getQueryObjectuiv); + + // GL_EXT_disjoint_timer_query + AssignGLExtensionEntryPoint(extensions, "GL_EXT_disjoint_timer_query", loadProcAddress("glGenQueriesEXT"), &genQueries); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_disjoint_timer_query", loadProcAddress("glDeleteQueriesEXT"), &deleteQueries); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_disjoint_timer_query", loadProcAddress("glIsQueryEXT"), &isQuery); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_disjoint_timer_query", loadProcAddress("glBeginQueryEXT"), &beginQuery); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_disjoint_timer_query", loadProcAddress("glEndQueryEXT"), &endQuery); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_disjoint_timer_query", loadProcAddress("glQueryCounterEXT"), &queryCounter); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_disjoint_timer_query", loadProcAddress("glGetQueryivEXT"), &getQueryiv); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_disjoint_timer_query", loadProcAddress("glGetQueryObjectivEXT"), &getQueryObjectiv); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_disjoint_timer_query", loadProcAddress("glGetQueryObjectuivEXT"), &getQueryObjectuiv); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_disjoint_timer_query", loadProcAddress("glGetQueryObjecti64vEXT"), &getQueryObjecti64v); + AssignGLExtensionEntryPoint(extensions, "GL_EXT_disjoint_timer_query", loadProcAddress("glGetQueryObjectui64vEXT"), &getQueryObjectui64v); + + // GL_OES_EGL_image + AssignGLExtensionEntryPoint(extensions, "GL_OES_EGL_image", loadProcAddress("glEGLImageTargetRenderbufferStorageOES"), &eglImageTargetRenderbufferStorageOES); + AssignGLExtensionEntryPoint(extensions, "GL_OES_EGL_image", loadProcAddress("glEGLImageTargetTexture2DOES"), &eglImageTargetTexture2DOES); + + // 2.0 + if (isAtLeastGLES(gl::Version(2, 0))) + { + AssignGLEntryPoint(loadProcAddress("glActiveTexture"), &activeTexture); + AssignGLEntryPoint(loadProcAddress("glAttachShader"), &attachShader); + AssignGLEntryPoint(loadProcAddress("glBindAttribLocation"), &bindAttribLocation); + AssignGLEntryPoint(loadProcAddress("glBindBuffer"), &bindBuffer); + AssignGLEntryPoint(loadProcAddress("glBindFramebuffer"), &bindFramebuffer); + AssignGLEntryPoint(loadProcAddress("glBindRenderbuffer"), &bindRenderbuffer); + AssignGLEntryPoint(loadProcAddress("glBindTexture"), &bindTexture); + AssignGLEntryPoint(loadProcAddress("glBlendColor"), &blendColor); + AssignGLEntryPoint(loadProcAddress("glBlendEquation"), &blendEquation); + AssignGLEntryPoint(loadProcAddress("glBlendEquationSeparate"), &blendEquationSeparate); + AssignGLEntryPoint(loadProcAddress("glBlendFunc"), &blendFunc); + AssignGLEntryPoint(loadProcAddress("glBlendFuncSeparate"), &blendFuncSeparate); + AssignGLEntryPoint(loadProcAddress("glBufferData"), &bufferData); + AssignGLEntryPoint(loadProcAddress("glBufferSubData"), &bufferSubData); + AssignGLEntryPoint(loadProcAddress("glCheckFramebufferStatus"), &checkFramebufferStatus); + AssignGLEntryPoint(loadProcAddress("glClear"), &clear); + AssignGLEntryPoint(loadProcAddress("glClearColor"), &clearColor); + AssignGLEntryPoint(loadProcAddress("glClearDepthf"), &clearDepthf); + AssignGLEntryPoint(loadProcAddress("glClearStencil"), &clearStencil); + AssignGLEntryPoint(loadProcAddress("glColorMask"), &colorMask); + AssignGLEntryPoint(loadProcAddress("glCompileShader"), &compileShader); + AssignGLEntryPoint(loadProcAddress("glCompressedTexImage2D"), &compressedTexImage2D); + AssignGLEntryPoint(loadProcAddress("glCompressedTexSubImage2D"), &compressedTexSubImage2D); + AssignGLEntryPoint(loadProcAddress("glCopyTexImage2D"), ©TexImage2D); + AssignGLEntryPoint(loadProcAddress("glCopyTexSubImage2D"), ©TexSubImage2D); + AssignGLEntryPoint(loadProcAddress("glCreateProgram"), &createProgram); + AssignGLEntryPoint(loadProcAddress("glCreateShader"), &createShader); + AssignGLEntryPoint(loadProcAddress("glCullFace"), &cullFace); + AssignGLEntryPoint(loadProcAddress("glDeleteBuffers"), &deleteBuffers); + AssignGLEntryPoint(loadProcAddress("glDeleteFramebuffers"), &deleteFramebuffers); + AssignGLEntryPoint(loadProcAddress("glDeleteProgram"), &deleteProgram); + AssignGLEntryPoint(loadProcAddress("glDeleteRenderbuffers"), &deleteRenderbuffers); + AssignGLEntryPoint(loadProcAddress("glDeleteShader"), &deleteShader); + AssignGLEntryPoint(loadProcAddress("glDeleteTextures"), &deleteTextures); + AssignGLEntryPoint(loadProcAddress("glDepthFunc"), &depthFunc); + AssignGLEntryPoint(loadProcAddress("glDepthMask"), &depthMask); + AssignGLEntryPoint(loadProcAddress("glDepthRangef"), &depthRangef); + AssignGLEntryPoint(loadProcAddress("glDetachShader"), &detachShader); + AssignGLEntryPoint(loadProcAddress("glDisable"), &disable); + AssignGLEntryPoint(loadProcAddress("glDisableVertexAttribArray"), &disableVertexAttribArray); + AssignGLEntryPoint(loadProcAddress("glDrawArrays"), &drawArrays); + AssignGLEntryPoint(loadProcAddress("glDrawElements"), &drawElements); + AssignGLEntryPoint(loadProcAddress("glEnable"), &enable); + AssignGLEntryPoint(loadProcAddress("glEnableVertexAttribArray"), &enableVertexAttribArray); + AssignGLEntryPoint(loadProcAddress("glFinish"), &finish); + AssignGLEntryPoint(loadProcAddress("glFlush"), &flush); + AssignGLEntryPoint(loadProcAddress("glFramebufferRenderbuffer"), &framebufferRenderbuffer); + AssignGLEntryPoint(loadProcAddress("glFramebufferTexture2D"), &framebufferTexture2D); + AssignGLEntryPoint(loadProcAddress("glFrontFace"), &frontFace); + AssignGLEntryPoint(loadProcAddress("glGenBuffers"), &genBuffers); + AssignGLEntryPoint(loadProcAddress("glGenerateMipmap"), &generateMipmap); + AssignGLEntryPoint(loadProcAddress("glGenFramebuffers"), &genFramebuffers); + AssignGLEntryPoint(loadProcAddress("glGenRenderbuffers"), &genRenderbuffers); + AssignGLEntryPoint(loadProcAddress("glGenTextures"), &genTextures); + AssignGLEntryPoint(loadProcAddress("glGetActiveAttrib"), &getActiveAttrib); + AssignGLEntryPoint(loadProcAddress("glGetActiveUniform"), &getActiveUniform); + AssignGLEntryPoint(loadProcAddress("glGetAttachedShaders"), &getAttachedShaders); + AssignGLEntryPoint(loadProcAddress("glGetAttribLocation"), &getAttribLocation); + AssignGLEntryPoint(loadProcAddress("glGetBooleanv"), &getBooleanv); + AssignGLEntryPoint(loadProcAddress("glGetBufferParameteriv"), &getBufferParameteriv); + AssignGLEntryPoint(loadProcAddress("glGetError"), &getError); + AssignGLEntryPoint(loadProcAddress("glGetFloatv"), &getFloatv); + AssignGLEntryPoint(loadProcAddress("glGetFramebufferAttachmentParameteriv"), &getFramebufferAttachmentParameteriv); + AssignGLEntryPoint(loadProcAddress("glGetIntegerv"), &getIntegerv); + AssignGLEntryPoint(loadProcAddress("glGetProgramiv"), &getProgramiv); + AssignGLEntryPoint(loadProcAddress("glGetProgramInfoLog"), &getProgramInfoLog); + AssignGLEntryPoint(loadProcAddress("glGetRenderbufferParameteriv"), &getRenderbufferParameteriv); + AssignGLEntryPoint(loadProcAddress("glGetShaderiv"), &getShaderiv); + AssignGLEntryPoint(loadProcAddress("glGetShaderInfoLog"), &getShaderInfoLog); + AssignGLEntryPoint(loadProcAddress("glGetShaderPrecisionFormat"), &getShaderPrecisionFormat); + AssignGLEntryPoint(loadProcAddress("glGetShaderSource"), &getShaderSource); + AssignGLEntryPoint(loadProcAddress("glGetString"), &getString); + AssignGLEntryPoint(loadProcAddress("glGetTexParameterfv"), &getTexParameterfv); + AssignGLEntryPoint(loadProcAddress("glGetTexParameteriv"), &getTexParameteriv); + AssignGLEntryPoint(loadProcAddress("glGetUniformfv"), &getUniformfv); + AssignGLEntryPoint(loadProcAddress("glGetUniformiv"), &getUniformiv); + AssignGLEntryPoint(loadProcAddress("glGetUniformLocation"), &getUniformLocation); + AssignGLEntryPoint(loadProcAddress("glGetVertexAttribfv"), &getVertexAttribfv); + AssignGLEntryPoint(loadProcAddress("glGetVertexAttribiv"), &getVertexAttribiv); + AssignGLEntryPoint(loadProcAddress("glGetVertexAttribPointerv"), &getVertexAttribPointerv); + AssignGLEntryPoint(loadProcAddress("glHint"), &hint); + AssignGLEntryPoint(loadProcAddress("glIsBuffer"), &isBuffer); + AssignGLEntryPoint(loadProcAddress("glIsEnabled"), &isEnabled); + AssignGLEntryPoint(loadProcAddress("glIsFramebuffer"), &isFramebuffer); + AssignGLEntryPoint(loadProcAddress("glIsProgram"), &isProgram); + AssignGLEntryPoint(loadProcAddress("glIsRenderbuffer"), &isRenderbuffer); + AssignGLEntryPoint(loadProcAddress("glIsShader"), &isShader); + AssignGLEntryPoint(loadProcAddress("glIsTexture"), &isTexture); + AssignGLEntryPoint(loadProcAddress("glLineWidth"), &lineWidth); + AssignGLEntryPoint(loadProcAddress("glLinkProgram"), &linkProgram); + AssignGLEntryPoint(loadProcAddress("glPixelStorei"), &pixelStorei); + AssignGLEntryPoint(loadProcAddress("glPolygonOffset"), &polygonOffset); + AssignGLEntryPoint(loadProcAddress("glReadPixels"), &readPixels); + AssignGLEntryPoint(loadProcAddress("glReleaseShaderCompiler"), &releaseShaderCompiler); + AssignGLEntryPoint(loadProcAddress("glRenderbufferStorage"), &renderbufferStorage); + AssignGLEntryPoint(loadProcAddress("glSampleCoverage"), &sampleCoverage); + AssignGLEntryPoint(loadProcAddress("glScissor"), &scissor); + AssignGLEntryPoint(loadProcAddress("glShaderBinary"), &shaderBinary); + AssignGLEntryPoint(loadProcAddress("glShaderSource"), &shaderSource); + AssignGLEntryPoint(loadProcAddress("glStencilFunc"), &stencilFunc); + AssignGLEntryPoint(loadProcAddress("glStencilFuncSeparate"), &stencilFuncSeparate); + AssignGLEntryPoint(loadProcAddress("glStencilMask"), &stencilMask); + AssignGLEntryPoint(loadProcAddress("glStencilMaskSeparate"), &stencilMaskSeparate); + AssignGLEntryPoint(loadProcAddress("glStencilOp"), &stencilOp); + AssignGLEntryPoint(loadProcAddress("glStencilOpSeparate"), &stencilOpSeparate); + AssignGLEntryPoint(loadProcAddress("glTexImage2D"), &texImage2D); + AssignGLEntryPoint(loadProcAddress("glTexParameterf"), &texParameterf); + AssignGLEntryPoint(loadProcAddress("glTexParameterfv"), &texParameterfv); + AssignGLEntryPoint(loadProcAddress("glTexParameteri"), &texParameteri); + AssignGLEntryPoint(loadProcAddress("glTexParameteriv"), &texParameteriv); + AssignGLEntryPoint(loadProcAddress("glTexSubImage2D"), &texSubImage2D); + AssignGLEntryPoint(loadProcAddress("glUniform1f"), &uniform1f); + AssignGLEntryPoint(loadProcAddress("glUniform1fv"), &uniform1fv); + AssignGLEntryPoint(loadProcAddress("glUniform1i"), &uniform1i); + AssignGLEntryPoint(loadProcAddress("glUniform1iv"), &uniform1iv); + AssignGLEntryPoint(loadProcAddress("glUniform2f"), &uniform2f); + AssignGLEntryPoint(loadProcAddress("glUniform2fv"), &uniform2fv); + AssignGLEntryPoint(loadProcAddress("glUniform2i"), &uniform2i); + AssignGLEntryPoint(loadProcAddress("glUniform2iv"), &uniform2iv); + AssignGLEntryPoint(loadProcAddress("glUniform3f"), &uniform3f); + AssignGLEntryPoint(loadProcAddress("glUniform3fv"), &uniform3fv); + AssignGLEntryPoint(loadProcAddress("glUniform3i"), &uniform3i); + AssignGLEntryPoint(loadProcAddress("glUniform3iv"), &uniform3iv); + AssignGLEntryPoint(loadProcAddress("glUniform4f"), &uniform4f); + AssignGLEntryPoint(loadProcAddress("glUniform4fv"), &uniform4fv); + AssignGLEntryPoint(loadProcAddress("glUniform4i"), &uniform4i); + AssignGLEntryPoint(loadProcAddress("glUniform4iv"), &uniform4iv); + AssignGLEntryPoint(loadProcAddress("glUniformMatrix2fv"), &uniformMatrix2fv); + AssignGLEntryPoint(loadProcAddress("glUniformMatrix3fv"), &uniformMatrix3fv); + AssignGLEntryPoint(loadProcAddress("glUniformMatrix4fv"), &uniformMatrix4fv); + AssignGLEntryPoint(loadProcAddress("glUseProgram"), &useProgram); + AssignGLEntryPoint(loadProcAddress("glValidateProgram"), &validateProgram); + AssignGLEntryPoint(loadProcAddress("glVertexAttrib1f"), &vertexAttrib1f); + AssignGLEntryPoint(loadProcAddress("glVertexAttrib1fv"), &vertexAttrib1fv); + AssignGLEntryPoint(loadProcAddress("glVertexAttrib2f"), &vertexAttrib2f); + AssignGLEntryPoint(loadProcAddress("glVertexAttrib2fv"), &vertexAttrib2fv); + AssignGLEntryPoint(loadProcAddress("glVertexAttrib3f"), &vertexAttrib3f); + AssignGLEntryPoint(loadProcAddress("glVertexAttrib3fv"), &vertexAttrib3fv); + AssignGLEntryPoint(loadProcAddress("glVertexAttrib4f"), &vertexAttrib4f); + AssignGLEntryPoint(loadProcAddress("glVertexAttrib4fv"), &vertexAttrib4fv); + AssignGLEntryPoint(loadProcAddress("glVertexAttribPointer"), &vertexAttribPointer); + AssignGLEntryPoint(loadProcAddress("glViewport"), &viewport); + } + + // 3.0 + if (isAtLeastGLES(gl::Version(3, 0))) + { + AssignGLEntryPoint(loadProcAddress("glReadBuffer"), &readBuffer); + AssignGLEntryPoint(loadProcAddress("glDrawRangeElements"), &drawRangeElements); + AssignGLEntryPoint(loadProcAddress("glTexImage3D"), &texImage3D); + AssignGLEntryPoint(loadProcAddress("glTexSubImage3D"), &texSubImage3D); + AssignGLEntryPoint(loadProcAddress("glCopyTexSubImage3D"), ©TexSubImage3D); + AssignGLEntryPoint(loadProcAddress("glCompressedTexImage3D"), &compressedTexImage3D); + AssignGLEntryPoint(loadProcAddress("glCompressedTexSubImage3D"), &compressedTexSubImage3D); + AssignGLEntryPoint(loadProcAddress("glGenQueries"), &genQueries); + AssignGLEntryPoint(loadProcAddress("glDeleteQueries"), &deleteQueries); + AssignGLEntryPoint(loadProcAddress("glIsQuery"), &isQuery); + AssignGLEntryPoint(loadProcAddress("glBeginQuery"), &beginQuery); + AssignGLEntryPoint(loadProcAddress("glEndQuery"), &endQuery); + AssignGLEntryPoint(loadProcAddress("glGetQueryiv"), &getQueryiv); + AssignGLEntryPoint(loadProcAddress("glGetQueryObjectuiv"), &getQueryObjectuiv); + AssignGLEntryPoint(loadProcAddress("glUnmapBuffer"), &unmapBuffer); + AssignGLEntryPoint(loadProcAddress("glGetBufferPointerv"), &getBufferPointerv); + AssignGLEntryPoint(loadProcAddress("glDrawBuffers"), &drawBuffers); + AssignGLEntryPoint(loadProcAddress("glUniformMatrix2x3fv"), &uniformMatrix2x3fv); + AssignGLEntryPoint(loadProcAddress("glUniformMatrix3x2fv"), &uniformMatrix3x2fv); + AssignGLEntryPoint(loadProcAddress("glUniformMatrix2x4fv"), &uniformMatrix2x4fv); + AssignGLEntryPoint(loadProcAddress("glUniformMatrix4x2fv"), &uniformMatrix4x2fv); + AssignGLEntryPoint(loadProcAddress("glUniformMatrix3x4fv"), &uniformMatrix3x4fv); + AssignGLEntryPoint(loadProcAddress("glUniformMatrix4x3fv"), &uniformMatrix4x3fv); + AssignGLEntryPoint(loadProcAddress("glBlitFramebuffer"), &blitFramebuffer); + AssignGLEntryPoint(loadProcAddress("glRenderbufferStorageMultisample"), &renderbufferStorageMultisample); + AssignGLEntryPoint(loadProcAddress("glFramebufferTextureLayer"), &framebufferTextureLayer); + AssignGLEntryPoint(loadProcAddress("glMapBufferRange"), &mapBufferRange); + AssignGLEntryPoint(loadProcAddress("glFlushMappedBufferRange"), &flushMappedBufferRange); + AssignGLEntryPoint(loadProcAddress("glBindVertexArray"), &bindVertexArray); + AssignGLEntryPoint(loadProcAddress("glDeleteVertexArrays"), &deleteVertexArrays); + AssignGLEntryPoint(loadProcAddress("glGenVertexArrays"), &genVertexArrays); + AssignGLEntryPoint(loadProcAddress("glIsVertexArray"), &isVertexArray); + AssignGLEntryPoint(loadProcAddress("glGetIntegeri_v"), &getIntegeri_v); + AssignGLEntryPoint(loadProcAddress("glBeginTransformFeedback"), &beginTransformFeedback); + AssignGLEntryPoint(loadProcAddress("glEndTransformFeedback"), &endTransformFeedback); + AssignGLEntryPoint(loadProcAddress("glBindBufferRange"), &bindBufferRange); + AssignGLEntryPoint(loadProcAddress("glBindBufferBase"), &bindBufferBase); + AssignGLEntryPoint(loadProcAddress("glTransformFeedbackVaryings"), &transformFeedbackVaryings); + AssignGLEntryPoint(loadProcAddress("glGetTransformFeedbackVarying"), &getTransformFeedbackVarying); + AssignGLEntryPoint(loadProcAddress("glVertexAttribIPointer"), &vertexAttribIPointer); + AssignGLEntryPoint(loadProcAddress("glGetVertexAttribIiv"), &getVertexAttribIiv); + AssignGLEntryPoint(loadProcAddress("glGetVertexAttribIuiv"), &getVertexAttribIuiv); + AssignGLEntryPoint(loadProcAddress("glVertexAttribI4i"), &vertexAttribI4i); + AssignGLEntryPoint(loadProcAddress("glVertexAttribI4ui"), &vertexAttribI4ui); + AssignGLEntryPoint(loadProcAddress("glVertexAttribI4iv"), &vertexAttribI4iv); + AssignGLEntryPoint(loadProcAddress("glVertexAttribI4uiv"), &vertexAttribI4uiv); + AssignGLEntryPoint(loadProcAddress("glGetUniformuiv"), &getUniformuiv); + AssignGLEntryPoint(loadProcAddress("glGetFragDataLocation"), &getFragDataLocation); + AssignGLEntryPoint(loadProcAddress("glUniform1ui"), &uniform1ui); + AssignGLEntryPoint(loadProcAddress("glUniform2ui"), &uniform2ui); + AssignGLEntryPoint(loadProcAddress("glUniform3ui"), &uniform3ui); + AssignGLEntryPoint(loadProcAddress("glUniform4ui"), &uniform4ui); + AssignGLEntryPoint(loadProcAddress("glUniform1uiv"), &uniform1uiv); + AssignGLEntryPoint(loadProcAddress("glUniform2uiv"), &uniform2uiv); + AssignGLEntryPoint(loadProcAddress("glUniform3uiv"), &uniform3uiv); + AssignGLEntryPoint(loadProcAddress("glUniform4uiv"), &uniform4uiv); + AssignGLEntryPoint(loadProcAddress("glClearBufferiv"), &clearBufferiv); + AssignGLEntryPoint(loadProcAddress("glClearBufferuiv"), &clearBufferuiv); + AssignGLEntryPoint(loadProcAddress("glClearBufferfv"), &clearBufferfv); + AssignGLEntryPoint(loadProcAddress("glClearBufferfi"), &clearBufferfi); + AssignGLEntryPoint(loadProcAddress("glGetStringi"), &getStringi); + AssignGLEntryPoint(loadProcAddress("glCopyBufferSubData"), ©BufferSubData); + AssignGLEntryPoint(loadProcAddress("glGetUniformIndices"), &getUniformIndices); + AssignGLEntryPoint(loadProcAddress("glGetActiveUniformsiv"), &getActiveUniformsiv); + AssignGLEntryPoint(loadProcAddress("glGetUniformBlockIndex"), &getUniformBlockIndex); + AssignGLEntryPoint(loadProcAddress("glGetActiveUniformBlockiv"), &getActiveUniformBlockiv); + AssignGLEntryPoint(loadProcAddress("glGetActiveUniformBlockName"), &getActiveUniformBlockName); + AssignGLEntryPoint(loadProcAddress("glUniformBlockBinding"), &uniformBlockBinding); + AssignGLEntryPoint(loadProcAddress("glDrawArraysInstanced"), &drawArraysInstanced); + AssignGLEntryPoint(loadProcAddress("glDrawElementsInstanced"), &drawElementsInstanced); + AssignGLEntryPoint(loadProcAddress("glFenceSync"), &fenceSync); + AssignGLEntryPoint(loadProcAddress("glIsSync"), &isSync); + AssignGLEntryPoint(loadProcAddress("glDeleteSync"), &deleteSync); + AssignGLEntryPoint(loadProcAddress("glClientWaitSync"), &clientWaitSync); + AssignGLEntryPoint(loadProcAddress("glWaitSync"), &waitSync); + AssignGLEntryPoint(loadProcAddress("glGetInteger64v"), &getInteger64v); + AssignGLEntryPoint(loadProcAddress("glGetSynciv"), &getSynciv); + AssignGLEntryPoint(loadProcAddress("glGetInteger64i_v"), &getInteger64i_v); + AssignGLEntryPoint(loadProcAddress("glGetBufferParameteri64v"), &getBufferParameteri64v); + AssignGLEntryPoint(loadProcAddress("glGenSamplers"), &genSamplers); + AssignGLEntryPoint(loadProcAddress("glDeleteSamplers"), &deleteSamplers); + AssignGLEntryPoint(loadProcAddress("glIsSampler"), &isSampler); + AssignGLEntryPoint(loadProcAddress("glBindSampler"), &bindSampler); + AssignGLEntryPoint(loadProcAddress("glSamplerParameteri"), &samplerParameteri); + AssignGLEntryPoint(loadProcAddress("glSamplerParameteriv"), &samplerParameteriv); + AssignGLEntryPoint(loadProcAddress("glSamplerParameterf"), &samplerParameterf); + AssignGLEntryPoint(loadProcAddress("glSamplerParameterfv"), &samplerParameterfv); + AssignGLEntryPoint(loadProcAddress("glGetSamplerParameteriv"), &getSamplerParameteriv); + AssignGLEntryPoint(loadProcAddress("glGetSamplerParameterfv"), &getSamplerParameterfv); + AssignGLEntryPoint(loadProcAddress("glVertexAttribDivisor"), &vertexAttribDivisor); + AssignGLEntryPoint(loadProcAddress("glBindTransformFeedback"), &bindTransformFeedback); + AssignGLEntryPoint(loadProcAddress("glDeleteTransformFeedbacks"), &deleteTransformFeedbacks); + AssignGLEntryPoint(loadProcAddress("glGenTransformFeedbacks"), &genTransformFeedbacks); + AssignGLEntryPoint(loadProcAddress("glIsTransformFeedback"), &isTransformFeedback); + AssignGLEntryPoint(loadProcAddress("glPauseTransformFeedback"), &pauseTransformFeedback); + AssignGLEntryPoint(loadProcAddress("glResumeTransformFeedback"), &resumeTransformFeedback); + AssignGLEntryPoint(loadProcAddress("glGetProgramBinary"), &getProgramBinary); + AssignGLEntryPoint(loadProcAddress("glProgramBinary"), &programBinary); + AssignGLEntryPoint(loadProcAddress("glProgramParameteri"), &programParameteri); + AssignGLEntryPoint(loadProcAddress("glInvalidateFramebuffer"), &invalidateFramebuffer); + AssignGLEntryPoint(loadProcAddress("glInvalidateSubFramebuffer"), &invalidateSubFramebuffer); + AssignGLEntryPoint(loadProcAddress("glTexStorage2D"), &texStorage2D); + AssignGLEntryPoint(loadProcAddress("glTexStorage3D"), &texStorage3D); + AssignGLEntryPoint(loadProcAddress("glGetInternalformativ"), &getInternalformativ); + } + + // 3.1 + if (isAtLeastGLES(gl::Version(3, 1))) + { + AssignGLEntryPoint(loadProcAddress("glDispatchCompute"), &dispatchCompute); + AssignGLEntryPoint(loadProcAddress("glDispatchComputeIndirect"), &dispatchComputeIndirect); + AssignGLEntryPoint(loadProcAddress("glDrawArraysIndirect"), &drawArraysIndirect); + AssignGLEntryPoint(loadProcAddress("glDrawElementsIndirect"), &drawElementsIndirect); + AssignGLEntryPoint(loadProcAddress("glFramebufferParameteri"), &framebufferParameteri); + AssignGLEntryPoint(loadProcAddress("glGetFramebufferParameteriv"), &getFramebufferParameteriv); + AssignGLEntryPoint(loadProcAddress("glGetProgramInterfaceiv"), &getProgramInterfaceiv); + AssignGLEntryPoint(loadProcAddress("glGetProgramResourceIndex"), &getProgramResourceIndex); + AssignGLEntryPoint(loadProcAddress("glGetProgramResourceName"), &getProgramResourceName); + AssignGLEntryPoint(loadProcAddress("glGetProgramResourceiv"), &getProgramResourceiv); + AssignGLEntryPoint(loadProcAddress("glGetProgramResourceLocation"), &getProgramResourceLocation); + AssignGLEntryPoint(loadProcAddress("glUseProgramStages"), &useProgramStages); + AssignGLEntryPoint(loadProcAddress("glActiveShaderProgram"), &activeShaderProgram); + AssignGLEntryPoint(loadProcAddress("glCreateShaderProgramv"), &createShaderProgramv); + AssignGLEntryPoint(loadProcAddress("glBindProgramPipeline"), &bindProgramPipeline); + AssignGLEntryPoint(loadProcAddress("glDeleteProgramPipelines"), &deleteProgramPipelines); + AssignGLEntryPoint(loadProcAddress("glGenProgramPipelines"), &genProgramPipelines); + AssignGLEntryPoint(loadProcAddress("glIsProgramPipeline"), &isProgramPipeline); + AssignGLEntryPoint(loadProcAddress("glGetProgramPipelineiv"), &getProgramPipelineiv); + AssignGLEntryPoint(loadProcAddress("glProgramUniform1i"), &programUniform1i); + AssignGLEntryPoint(loadProcAddress("glProgramUniform2i"), &programUniform2i); + AssignGLEntryPoint(loadProcAddress("glProgramUniform3i"), &programUniform3i); + AssignGLEntryPoint(loadProcAddress("glProgramUniform4i"), &programUniform4i); + AssignGLEntryPoint(loadProcAddress("glProgramUniform1ui"), &programUniform1ui); + AssignGLEntryPoint(loadProcAddress("glProgramUniform2ui"), &programUniform2ui); + AssignGLEntryPoint(loadProcAddress("glProgramUniform3ui"), &programUniform3ui); + AssignGLEntryPoint(loadProcAddress("glProgramUniform4ui"), &programUniform4ui); + AssignGLEntryPoint(loadProcAddress("glProgramUniform1f"), &programUniform1f); + AssignGLEntryPoint(loadProcAddress("glProgramUniform2f"), &programUniform2f); + AssignGLEntryPoint(loadProcAddress("glProgramUniform3f"), &programUniform3f); + AssignGLEntryPoint(loadProcAddress("glProgramUniform4f"), &programUniform4f); + AssignGLEntryPoint(loadProcAddress("glProgramUniform1iv"), &programUniform1iv); + AssignGLEntryPoint(loadProcAddress("glProgramUniform2iv"), &programUniform2iv); + AssignGLEntryPoint(loadProcAddress("glProgramUniform3iv"), &programUniform3iv); + AssignGLEntryPoint(loadProcAddress("glProgramUniform4iv"), &programUniform4iv); + AssignGLEntryPoint(loadProcAddress("glProgramUniform1uiv"), &programUniform1uiv); + AssignGLEntryPoint(loadProcAddress("glProgramUniform2uiv"), &programUniform2uiv); + AssignGLEntryPoint(loadProcAddress("glProgramUniform3uiv"), &programUniform3uiv); + AssignGLEntryPoint(loadProcAddress("glProgramUniform4uiv"), &programUniform4uiv); + AssignGLEntryPoint(loadProcAddress("glProgramUniform1fv"), &programUniform1fv); + AssignGLEntryPoint(loadProcAddress("glProgramUniform2fv"), &programUniform2fv); + AssignGLEntryPoint(loadProcAddress("glProgramUniform3fv"), &programUniform3fv); + AssignGLEntryPoint(loadProcAddress("glProgramUniform4fv"), &programUniform4fv); + AssignGLEntryPoint(loadProcAddress("glProgramUniformMatrix2fv"), &programUniformMatrix2fv); + AssignGLEntryPoint(loadProcAddress("glProgramUniformMatrix3fv"), &programUniformMatrix3fv); + AssignGLEntryPoint(loadProcAddress("glProgramUniformMatrix4fv"), &programUniformMatrix4fv); + AssignGLEntryPoint(loadProcAddress("glProgramUniformMatrix2x3fv"), &programUniformMatrix2x3fv); + AssignGLEntryPoint(loadProcAddress("glProgramUniformMatrix3x2fv"), &programUniformMatrix3x2fv); + AssignGLEntryPoint(loadProcAddress("glProgramUniformMatrix2x4fv"), &programUniformMatrix2x4fv); + AssignGLEntryPoint(loadProcAddress("glProgramUniformMatrix4x2fv"), &programUniformMatrix4x2fv); + AssignGLEntryPoint(loadProcAddress("glProgramUniformMatrix3x4fv"), &programUniformMatrix3x4fv); + AssignGLEntryPoint(loadProcAddress("glProgramUniformMatrix4x3fv"), &programUniformMatrix4x3fv); + AssignGLEntryPoint(loadProcAddress("glValidateProgramPipeline"), &validateProgramPipeline); + AssignGLEntryPoint(loadProcAddress("glGetProgramPipelineInfoLog"), &getProgramPipelineInfoLog); + AssignGLEntryPoint(loadProcAddress("glBindImageTexture"), &bindImageTexture); + AssignGLEntryPoint(loadProcAddress("glGetBooleani_v"), &getBooleani_v); + AssignGLEntryPoint(loadProcAddress("glMemoryBarrier"), &memoryBarrier); + AssignGLEntryPoint(loadProcAddress("glMemoryBarrierByRegion"), &memoryBarrierByRegion); + AssignGLEntryPoint(loadProcAddress("glTexStorage2DMultisample"), &texStorage2DMultisample); + AssignGLEntryPoint(loadProcAddress("glGetMultisamplefv"), &getMultisamplefv); + AssignGLEntryPoint(loadProcAddress("glSampleMaski"), &sampleMaski); + AssignGLEntryPoint(loadProcAddress("glGetTexLevelParameteriv"), &getTexLevelParameteriv); + AssignGLEntryPoint(loadProcAddress("glGetTexLevelParameterfv"), &getTexLevelParameterfv); + AssignGLEntryPoint(loadProcAddress("glBindVertexBuffer"), &bindVertexBuffer); + AssignGLEntryPoint(loadProcAddress("glVertexAttribFormat"), &vertexAttribFormat); + AssignGLEntryPoint(loadProcAddress("glVertexAttribIFormat"), &vertexAttribIFormat); + AssignGLEntryPoint(loadProcAddress("glVertexAttribBinding"), &vertexAttribBinding); + AssignGLEntryPoint(loadProcAddress("glVertexBindingDivisor"), &vertexBindingDivisor); + } + + // 3.2 + if (isAtLeastGLES(gl::Version(3, 2))) + { + AssignGLEntryPoint(loadProcAddress("glBlendBarrier"), &blendBarrier); + AssignGLEntryPoint(loadProcAddress("glCopyImageSubData"), ©ImageSubData); + AssignGLEntryPoint(loadProcAddress("glDebugMessageControl"), &debugMessageControl); + AssignGLEntryPoint(loadProcAddress("glDebugMessageInsert"), &debugMessageInsert); + AssignGLEntryPoint(loadProcAddress("glDebugMessageCallback"), &debugMessageCallback); + AssignGLEntryPoint(loadProcAddress("glGetDebugMessageLog"), &getDebugMessageLog); + AssignGLEntryPoint(loadProcAddress("glPushDebugGroup"), &pushDebugGroup); + AssignGLEntryPoint(loadProcAddress("glPopDebugGroup"), &popDebugGroup); + AssignGLEntryPoint(loadProcAddress("glObjectLabel"), &objectLabel); + AssignGLEntryPoint(loadProcAddress("glGetObjectLabel"), &getObjectLabel); + AssignGLEntryPoint(loadProcAddress("glObjectPtrLabel"), &objectPtrLabel); + AssignGLEntryPoint(loadProcAddress("glGetObjectPtrLabel"), &getObjectPtrLabel); + AssignGLEntryPoint(loadProcAddress("glGetPointerv"), &getPointerv); + AssignGLEntryPoint(loadProcAddress("glEnablei"), &enablei); + AssignGLEntryPoint(loadProcAddress("glDisablei"), &disablei); + AssignGLEntryPoint(loadProcAddress("glBlendEquationi"), &blendEquationi); + AssignGLEntryPoint(loadProcAddress("glBlendEquationSeparatei"), &blendEquationSeparatei); + AssignGLEntryPoint(loadProcAddress("glBlendFunci"), &blendFunci); + AssignGLEntryPoint(loadProcAddress("glBlendFuncSeparatei"), &blendFuncSeparatei); + AssignGLEntryPoint(loadProcAddress("glColorMaski"), &colorMaski); + AssignGLEntryPoint(loadProcAddress("glIsEnabledi"), &isEnabledi); + AssignGLEntryPoint(loadProcAddress("glDrawElementsBaseVertex"), &drawElementsBaseVertex); + AssignGLEntryPoint(loadProcAddress("glDrawRangeElementsBaseVertex"), &drawRangeElementsBaseVertex); + AssignGLEntryPoint(loadProcAddress("glDrawElementsInstancedBaseVertex"), &drawElementsInstancedBaseVertex); + AssignGLEntryPoint(loadProcAddress("glFramebufferTexture"), &framebufferTexture); + AssignGLEntryPoint(loadProcAddress("glPrimitiveBoundingBox"), &primitiveBoundingBox); + AssignGLEntryPoint(loadProcAddress("glGetGraphicsResetStatus"), &getGraphicsResetStatus); + AssignGLEntryPoint(loadProcAddress("glReadnPixels"), &readnPixels); + AssignGLEntryPoint(loadProcAddress("glGetnUniformfv"), &getnUniformfv); + AssignGLEntryPoint(loadProcAddress("glGetnUniformiv"), &getnUniformiv); + AssignGLEntryPoint(loadProcAddress("glGetnUniformuiv"), &getnUniformuiv); + AssignGLEntryPoint(loadProcAddress("glMinSampleShading"), &minSampleShading); + AssignGLEntryPoint(loadProcAddress("glPatchParameteri"), &patchParameteri); + AssignGLEntryPoint(loadProcAddress("glTexParameterIiv"), &texParameterIiv); + AssignGLEntryPoint(loadProcAddress("glTexParameterIuiv"), &texParameterIuiv); + AssignGLEntryPoint(loadProcAddress("glGetTexParameterIiv"), &getTexParameterIiv); + AssignGLEntryPoint(loadProcAddress("glGetTexParameterIuiv"), &getTexParameterIuiv); + AssignGLEntryPoint(loadProcAddress("glSamplerParameterIiv"), &samplerParameterIiv); + AssignGLEntryPoint(loadProcAddress("glSamplerParameterIuiv"), &samplerParameterIuiv); + AssignGLEntryPoint(loadProcAddress("glGetSamplerParameterIiv"), &getSamplerParameterIiv); + AssignGLEntryPoint(loadProcAddress("glGetSamplerParameterIuiv"), &getSamplerParameterIuiv); + AssignGLEntryPoint(loadProcAddress("glTexBuffer"), &texBuffer); + AssignGLEntryPoint(loadProcAddress("glTexBufferRange"), &texBufferRange); + AssignGLEntryPoint(loadProcAddress("glTexStorage3DMultisample"), &texStorage3DMultisample); + } + + // clang-format on +} + bool FunctionsGL::isAtLeastGL(const gl::Version &glVersion) const { return standard == STANDARD_GL_DESKTOP && version >= glVersion; diff --git a/gfx/angle/src/libANGLE/renderer/gl/FunctionsGL.h b/gfx/angle/src/libANGLE/renderer/gl/FunctionsGL.h index 1dce040da8..c33c1a01c5 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/FunctionsGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/FunctionsGL.h @@ -743,7 +743,18 @@ class FunctionsGL PFNGLVERTEXARRAYVERTEXBUFFERPROC vertexArrayVertexBuffer; PFNGLVERTEXARRAYVERTEXBUFFERSPROC vertexArrayVertexBuffers; + // ES 3.2 + PFNGLBLENDBARRIERPROC blendBarrier; + PFNGLPRIMITIVEBOUNDINGBOXPROC primitiveBoundingBox; + + // ES extensions + PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC eglImageTargetRenderbufferStorageOES; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC eglImageTargetTexture2DOES; + private: + void initializeProcsDesktopGL(); + void initializeProcsGLES(); + virtual void *loadProcAddress(const std::string &function) = 0; }; diff --git a/gfx/angle/src/libANGLE/renderer/gl/ProgramGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/ProgramGL.cpp index 80d8c92be6..f5adc3b29b 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/ProgramGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/ProgramGL.cpp @@ -13,6 +13,7 @@ #include "libANGLE/renderer/gl/FunctionsGL.h" #include "libANGLE/renderer/gl/ShaderGL.h" #include "libANGLE/renderer/gl/StateManagerGL.h" +#include "platform/Platform.h" namespace rx { @@ -46,6 +47,11 @@ gl::Error ProgramGL::save(gl::BinaryOutputStream *stream) return gl::Error(GL_INVALID_OPERATION); } +void ProgramGL::setBinaryRetrievableHint(bool retrievable) +{ + UNIMPLEMENTED(); +} + LinkResult ProgramGL::link(const gl::Data &data, gl::InfoLog &infoLog) { // Reset the program state, delete the current program if one exists @@ -105,7 +111,6 @@ LinkResult ProgramGL::link(const gl::Data &data, gl::InfoLog &infoLog) // Verify the link GLint linkStatus = GL_FALSE; mFunctions->getProgramiv(mProgramID, GL_LINK_STATUS, &linkStatus); - ASSERT(linkStatus == GL_TRUE); if (linkStatus == GL_FALSE) { // Linking failed, put the error into the info log @@ -118,8 +123,11 @@ LinkResult ProgramGL::link(const gl::Data &data, gl::InfoLog &infoLog) mFunctions->deleteProgram(mProgramID); mProgramID = 0; - infoLog << &buf[0]; - TRACE("\n%s", &buf[0]); + infoLog << buf.data(); + + std::string warning = FormatString("Program link failed unexpectedly: %s", buf.data()); + ANGLEPlatformCurrent()->logWarning(warning.c_str()); + TRACE("\n%s", warning.c_str()); // TODO, return GL_OUT_OF_MEMORY or just fail the link? This is an unexpected case return LinkResult(false, gl::Error(GL_NO_ERROR)); diff --git a/gfx/angle/src/libANGLE/renderer/gl/ProgramGL.h b/gfx/angle/src/libANGLE/renderer/gl/ProgramGL.h index 377ba0f893..fa343688a6 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/ProgramGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/ProgramGL.h @@ -33,6 +33,7 @@ class ProgramGL : public ProgramImpl LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) override; gl::Error save(gl::BinaryOutputStream *stream) override; + void setBinaryRetrievableHint(bool retrievable) override; LinkResult link(const gl::Data &data, gl::InfoLog &infoLog) override; GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override; diff --git a/gfx/angle/src/libANGLE/renderer/gl/QueryGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/QueryGL.cpp index a8c9a9b988..6d9df5d760 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/QueryGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/QueryGL.cpp @@ -15,7 +15,7 @@ namespace { -GLuint MergeQueryResults(GLenum type, GLuint currentResult, GLuint newResult) +GLuint64 MergeQueryResults(GLenum type, GLuint64 currentResult, GLuint64 newResult) { switch (type) { @@ -26,6 +26,12 @@ GLuint MergeQueryResults(GLenum type, GLuint currentResult, GLuint newResult) case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: return currentResult + newResult; + case GL_TIME_ELAPSED: + return currentResult + newResult; + + case GL_TIMESTAMP: + return newResult; + default: UNREACHABLE(); return 0; @@ -62,7 +68,8 @@ QueryGL::~QueryGL() gl::Error QueryGL::begin() { mResultSum = 0; - return gl::Error(GL_NO_ERROR); + mStateManager->onBeginQuery(this); + return resume(); } gl::Error QueryGL::end() @@ -70,7 +77,22 @@ gl::Error QueryGL::end() return pause(); } -gl::Error QueryGL::getResult(GLuint *params) +gl::Error QueryGL::queryCounter() +{ + ASSERT(mType == GL_TIMESTAMP); + + // Directly create a query for the timestamp and add it to the pending query queue, as timestamp + // queries do not have the traditional begin/end block and never need to be paused/resumed + GLuint query; + mFunctions->genQueries(1, &query); + mFunctions->queryCounter(query, GL_TIMESTAMP); + mPendingQueries.push_back(query); + + return gl::Error(GL_NO_ERROR); +} + +template +gl::Error QueryGL::getResultBase(T *params) { ASSERT(mActiveQuery == 0); @@ -81,12 +103,32 @@ gl::Error QueryGL::getResult(GLuint *params) } ASSERT(mPendingQueries.empty()); - *params = mResultSum; + *params = static_cast(mResultSum); return gl::Error(GL_NO_ERROR); } -gl::Error QueryGL::isResultAvailable(GLuint *available) +gl::Error QueryGL::getResult(GLint *params) +{ + return getResultBase(params); +} + +gl::Error QueryGL::getResult(GLuint *params) +{ + return getResultBase(params); +} + +gl::Error QueryGL::getResult(GLint64 *params) +{ + return getResultBase(params); +} + +gl::Error QueryGL::getResult(GLuint64 *params) +{ + return getResultBase(params); +} + +gl::Error QueryGL::isResultAvailable(bool *available) { ASSERT(mActiveQuery == 0); @@ -96,7 +138,7 @@ gl::Error QueryGL::isResultAvailable(GLuint *available) return error; } - *available = mPendingQueries.empty() ? GL_TRUE : GL_FALSE; + *available = mPendingQueries.empty(); return gl::Error(GL_NO_ERROR); } @@ -153,9 +195,21 @@ gl::Error QueryGL::flush(bool force) } } - GLuint result = 0; - mFunctions->getQueryObjectuiv(id, GL_QUERY_RESULT, &result); - mResultSum = MergeQueryResults(mType, mResultSum, result); + // Even though getQueryObjectui64v was introduced for timer queries, there is nothing in the + // standard that says that it doesn't work for any other queries. It also passes on all the + // trybots, so we use it if it is available + if (mFunctions->getQueryObjectui64v != nullptr) + { + GLuint64 result = 0; + mFunctions->getQueryObjectui64v(id, GL_QUERY_RESULT, &result); + mResultSum = MergeQueryResults(mType, mResultSum, result); + } + else + { + GLuint result = 0; + mFunctions->getQueryObjectuiv(id, GL_QUERY_RESULT, &result); + mResultSum = MergeQueryResults(mType, mResultSum, static_cast(result)); + } mStateManager->deleteQuery(id); diff --git a/gfx/angle/src/libANGLE/renderer/gl/QueryGL.h b/gfx/angle/src/libANGLE/renderer/gl/QueryGL.h index 7385ff9dcf..5e280b1f7b 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/QueryGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/QueryGL.h @@ -27,8 +27,12 @@ class QueryGL : public QueryImpl gl::Error begin() override; gl::Error end() override; + gl::Error queryCounter() override; + gl::Error getResult(GLint *params) override; gl::Error getResult(GLuint *params) override; - gl::Error isResultAvailable(GLuint *available) override; + gl::Error getResult(GLint64 *params) override; + gl::Error getResult(GLuint64 *params) override; + gl::Error isResultAvailable(bool *available) override; // OpenGL is only allowed to have one query of each type active at any given time. Since ANGLE // virtualizes contexts, queries need to be able to be paused and resumed. @@ -42,6 +46,9 @@ class QueryGL : public QueryImpl private: gl::Error flush(bool force); + template + gl::Error getResultBase(T *params); + GLenum mType; const FunctionsGL *mFunctions; @@ -49,7 +56,7 @@ class QueryGL : public QueryImpl GLuint mActiveQuery; std::deque mPendingQueries; - GLuint mResultSum; + GLuint64 mResultSum; }; } diff --git a/gfx/angle/src/libANGLE/renderer/gl/RendererGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/RendererGL.cpp index d5e13258fc..1cda94bb9d 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/RendererGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/RendererGL.cpp @@ -363,12 +363,6 @@ bool RendererGL::testDeviceResettable() return bool(); } -VendorID RendererGL::getVendorId() const -{ - UNIMPLEMENTED(); - return VendorID(); -} - std::string RendererGL::getVendorString() const { return std::string(reinterpret_cast(mFunctions->getString(GL_VENDOR))); @@ -422,4 +416,23 @@ void RendererGL::syncState(const gl::State &state, const gl::State::DirtyBits &d { mStateManager->syncState(state, dirtyBits); } + +GLint RendererGL::getGPUDisjoint() +{ + // TODO(ewell): On GLES backends we should find a way to reliably query disjoint events + return 0; +} + +GLint64 RendererGL::getTimestamp() +{ + GLint64 result = 0; + mFunctions->getInteger64v(GL_TIMESTAMP, &result); + return result; +} + +void RendererGL::onMakeCurrent(const gl::Data &data) +{ + // Queries need to be paused/resumed on context switches + mStateManager->onMakeCurrent(data); +} } diff --git a/gfx/angle/src/libANGLE/renderer/gl/RendererGL.h b/gfx/angle/src/libANGLE/renderer/gl/RendererGL.h index 6583a1a790..0a66fedbc2 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/RendererGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/RendererGL.h @@ -99,12 +99,16 @@ class RendererGL : public Renderer bool testDeviceLost() override; bool testDeviceResettable() override; - VendorID getVendorId() const override; std::string getVendorString() const override; std::string getRendererDescription() const override; void syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits) override; + GLint getGPUDisjoint() override; + GLint64 getTimestamp() override; + + void onMakeCurrent(const gl::Data &data) override; + const gl::Version &getMaxSupportedESVersion() const; const FunctionsGL *getFunctions() const { return mFunctions; } StateManagerGL *getStateManager() const { return mStateManager; } diff --git a/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.cpp index 9976e5bb5a..a1e4b466a0 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.cpp @@ -28,7 +28,7 @@ namespace rx { static const GLenum QueryTypes[] = {GL_ANY_SAMPLES_PASSED, GL_ANY_SAMPLES_PASSED_CONSERVATIVE, - GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN}; + GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, GL_TIME_ELAPSED}; StateManagerGL::IndexedBufferBinding::IndexedBufferBinding() : offset(0), size(0), buffer(0) { @@ -47,7 +47,7 @@ StateManagerGL::StateManagerGL(const FunctionsGL *functions, const gl::Caps &ren mTransformFeedback(0), mQueries(), mPrevDrawTransformFeedback(nullptr), - mPrevDrawQueries(), + mCurrentQueries(), mPrevDrawContext(0), mUnpackAlignment(4), mUnpackRowLength(0), @@ -577,9 +577,14 @@ void StateManagerGL::endQuery(GLenum type, GLuint query) mFunctions->endQuery(type); } +void StateManagerGL::onBeginQuery(QueryGL *query) +{ + mCurrentQueries.insert(query); +} + void StateManagerGL::onDeleteQueryObject(QueryGL *query) { - mPrevDrawQueries.erase(query); + mCurrentQueries.erase(query); } gl::Error StateManagerGL::setDrawArraysState(const gl::Data &data, @@ -633,7 +638,7 @@ gl::Error StateManagerGL::setDrawElementsState(const gl::Data &data, return setGenericDrawState(data); } -gl::Error StateManagerGL::setGenericDrawState(const gl::Data &data) +gl::Error StateManagerGL::onMakeCurrent(const gl::Data &data) { const gl::State &state = *data.state; @@ -645,16 +650,35 @@ gl::Error StateManagerGL::setGenericDrawState(const gl::Data &data) mPrevDrawTransformFeedback->syncPausedState(true); } - for (QueryGL *prevQuery : mPrevDrawQueries) + for (QueryGL *prevQuery : mCurrentQueries) { prevQuery->pause(); } } + mCurrentQueries.clear(); mPrevDrawTransformFeedback = nullptr; - mPrevDrawQueries.clear(); - mPrevDrawContext = data.context; + // Set the current query state + for (GLenum queryType : QueryTypes) + { + gl::Query *query = state.getActiveQuery(queryType); + if (query != nullptr) + { + QueryGL *queryGL = GetImplAs(query); + queryGL->resume(); + + mCurrentQueries.insert(queryGL); + } + } + + return gl::Error(GL_NO_ERROR); +} + +gl::Error StateManagerGL::setGenericDrawState(const gl::Data &data) +{ + const gl::State &state = *data.state; + // Sync the current program state const gl::Program *program = state.getProgram(); const ProgramGL *programGL = GetImplAs(program); @@ -751,19 +775,6 @@ gl::Error StateManagerGL::setGenericDrawState(const gl::Data &data) mPrevDrawTransformFeedback = nullptr; } - // Set the current query state - for (GLenum queryType : QueryTypes) - { - gl::Query *query = state.getActiveQuery(queryType); - if (query != nullptr) - { - QueryGL *queryGL = GetImplAs(query); - queryGL->resume(); - - mPrevDrawQueries.insert(queryGL); - } - } - return gl::Error(GL_NO_ERROR); } @@ -1274,10 +1285,17 @@ void StateManagerGL::setClearStencil(GLint clearStencil) } } -void StateManagerGL::syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits) +void StateManagerGL::syncState(const gl::State &state, const gl::State::DirtyBits &glDirtyBits) { + const auto &glAndLocalDirtyBits = (glDirtyBits | mLocalDirtyBits); + + if (!glAndLocalDirtyBits.any()) + { + return; + } + // TODO(jmadill): Investigate only syncing vertex state for active attributes - for (unsigned int dirtyBit : angle::IterateBitSet(dirtyBits | mLocalDirtyBits)) + for (auto dirtyBit : angle::IterateBitSet(glAndLocalDirtyBits)) { switch (dirtyBit) { @@ -1469,30 +1487,18 @@ void StateManagerGL::syncState(const gl::State &state, const gl::State::DirtyBit case gl::State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING: // TODO(jmadill): implement this break; - case gl::State::DIRTY_BIT_READ_FRAMEBUFFER_OBJECT: - // TODO(jmadill): implement this - break; case gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING: // TODO(jmadill): implement this break; - case gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_OBJECT: - // TODO(jmadill): implement this - break; case gl::State::DIRTY_BIT_RENDERBUFFER_BINDING: // TODO(jmadill): implement this break; case gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING: // TODO(jmadill): implement this break; - case gl::State::DIRTY_BIT_VERTEX_ARRAY_OBJECT: - state.getVertexArray()->syncImplState(); - break; case gl::State::DIRTY_BIT_PROGRAM_BINDING: // TODO(jmadill): implement this break; - case gl::State::DIRTY_BIT_PROGRAM_OBJECT: - // TODO(jmadill): implement this - break; default: { ASSERT(dirtyBit >= gl::State::DIRTY_BIT_CURRENT_VALUE_0 && diff --git a/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.h b/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.h index b430208ae2..1e4cdd7f61 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.h @@ -59,6 +59,7 @@ class StateManagerGL final : angle::NonCopyable void bindTransformFeedback(GLenum type, GLuint transformFeedback); void beginQuery(GLenum type, GLuint query); void endQuery(GLenum type, GLuint query); + void onBeginQuery(QueryGL *query); void setAttributeCurrentData(size_t index, const gl::VertexAttribCurrentValueData &data); @@ -135,7 +136,9 @@ class StateManagerGL final : angle::NonCopyable GLsizei instanceCount, const GLvoid **outIndices); - void syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits); + gl::Error onMakeCurrent(const gl::Data &data); + + void syncState(const gl::State &state, const gl::State::DirtyBits &glDirtyBits); private: gl::Error setGenericDrawState(const gl::Data &data); @@ -170,7 +173,7 @@ class StateManagerGL final : angle::NonCopyable std::map mQueries; TransformFeedbackGL *mPrevDrawTransformFeedback; - std::set mPrevDrawQueries; + std::set mCurrentQueries; uintptr_t mPrevDrawContext; GLint mUnpackAlignment; diff --git a/gfx/angle/src/libANGLE/renderer/gl/VertexArrayGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/VertexArrayGL.cpp index 708566370b..255b41f565 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/VertexArrayGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/VertexArrayGL.cpp @@ -232,7 +232,7 @@ void VertexArrayGL::computeStreamingAttributeSizes(const gl::AttributesMask &act ASSERT(mAttributesNeedStreaming.any()); const auto &attribs = mData.getVertexAttributes(); - for (unsigned int idx : angle::IterateBitSet(mAttributesNeedStreaming & activeAttributesMask)) + for (auto idx : angle::IterateBitSet(mAttributesNeedStreaming & activeAttributesMask)) { const auto &attrib = attribs[idx]; ASSERT(AttributeNeedsStreaming(attrib)); @@ -292,8 +292,7 @@ gl::Error VertexArrayGL::streamAttributes(const gl::AttributesMask &activeAttrib size_t curBufferOffset = bufferEmptySpace; const auto &attribs = mData.getVertexAttributes(); - for (unsigned int idx : - angle::IterateBitSet(mAttributesNeedStreaming & activeAttributesMask)) + for (auto idx : angle::IterateBitSet(mAttributesNeedStreaming & activeAttributesMask)) { const auto &attrib = attribs[idx]; ASSERT(AttributeNeedsStreaming(attrib)); @@ -334,13 +333,14 @@ gl::Error VertexArrayGL::streamAttributes(const gl::AttributesMask &activeAttrib { ASSERT(!attrib.normalized); mFunctions->vertexAttribIPointer( - idx, attrib.size, attrib.type, static_cast(destStride), + static_cast(idx), attrib.size, attrib.type, + static_cast(destStride), reinterpret_cast(vertexStartOffset)); } else { mFunctions->vertexAttribPointer( - idx, attrib.size, attrib.type, attrib.normalized, + static_cast(idx), attrib.size, attrib.type, attrib.normalized, static_cast(destStride), reinterpret_cast(vertexStartOffset)); } diff --git a/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.h b/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.h index 280229d715..718713210c 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.h @@ -50,6 +50,11 @@ class DisplayCGL : public DisplayGL std::string getVendorString() const override; + egl::Error waitClient() const override; + egl::Error waitNative(EGLint engine, + egl::Surface *drawSurface, + egl::Surface *readSurface) const override; + private: const FunctionsGL *getFunctionsGL() const override; diff --git a/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm b/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm index 9037bbdf9a..de25a7b85a 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm +++ b/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm @@ -6,15 +6,15 @@ // DisplayCGL.mm: CGL implementation of egl::Display -#include "libANGLE/renderer/gl/CGL/DisplayCGL.h" +#include "libANGLE/renderer/gl/cgl/DisplayCGL.h" #import #include #include #include "common/debug.h" -#include "libANGLE/renderer/gl/CGL/PbufferSurfaceCGL.h" -#include "libANGLE/renderer/gl/CGL/WindowSurfaceCGL.h" +#include "libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h" +#include "libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h" namespace { @@ -33,7 +33,7 @@ class FunctionsGLCGL : public FunctionsGL public: FunctionsGLCGL(void *dylibHandle) : mDylibHandle(dylibHandle) {} - virtual ~FunctionsGLCGL() { dlclose(mDylibHandle); } + ~FunctionsGLCGL() override { dlclose(mDylibHandle); } private: void *loadProcAddress(const std::string &function) override @@ -113,7 +113,7 @@ SurfaceImpl *DisplayCGL::createWindowSurface(const egl::Config *configuration, EGLNativeWindowType window, const egl::AttributeMap &attribs) { - return new WindowSurfaceCGL(this->getRenderer(), window, mFunctions); + return new WindowSurfaceCGL(this->getRenderer(), window, mFunctions, mContext); } SurfaceImpl *DisplayCGL::createPbufferSurface(const egl::Config *configuration, @@ -248,6 +248,7 @@ const FunctionsGL *DisplayCGL::getFunctionsGL() const void DisplayCGL::generateExtensions(egl::DisplayExtensions *outExtensions) const { outExtensions->createContext = true; + outExtensions->createContextNoError = true; } void DisplayCGL::generateCaps(egl::Caps *outCaps) const @@ -255,4 +256,17 @@ void DisplayCGL::generateCaps(egl::Caps *outCaps) const outCaps->textureNPOT = true; } +egl::Error DisplayCGL::waitClient() const +{ + // TODO(cwallez) UNIMPLEMENTED() + return egl::Error(EGL_SUCCESS); +} + +egl::Error DisplayCGL::waitNative(EGLint engine, + egl::Surface *drawSurface, + egl::Surface *readSurface) const +{ + // TODO(cwallez) UNIMPLEMENTED() + return egl::Error(EGL_SUCCESS); +} } diff --git a/gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h b/gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h index 7bfd488ec6..80b5f58ecd 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h @@ -34,7 +34,7 @@ class PbufferSurfaceCGL : public SurfaceGL egl::Error swap() override; egl::Error postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) override; egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override; - egl::Error bindTexImage(EGLint buffer) override; + egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override; egl::Error releaseTexImage(EGLint buffer) override; void setSwapInterval(EGLint interval) override; diff --git a/gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.mm b/gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.mm index b1cff47e66..ec936a60a4 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.mm +++ b/gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.mm @@ -95,7 +95,7 @@ egl::Error PbufferSurfaceCGL::querySurfacePointerANGLE(EGLint attribute, void ** return egl::Error(EGL_SUCCESS); } -egl::Error PbufferSurfaceCGL::bindTexImage(EGLint buffer) +egl::Error PbufferSurfaceCGL::bindTexImage(gl::Texture *texture, EGLint buffer) { UNIMPLEMENTED(); return egl::Error(EGL_SUCCESS); diff --git a/gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h b/gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h index a6e0a0900e..83cbe8f092 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h @@ -11,10 +11,14 @@ #include "libANGLE/renderer/gl/SurfaceGL.h" +struct _CGLContextObject; +typedef _CGLContextObject *CGLContextObj; @class CALayer; struct __IOSurface; typedef __IOSurface *IOSurfaceRef; +@class SwapLayer; + namespace rx { @@ -24,12 +28,36 @@ class FunctionsGL; class StateManagerGL; struct WorkaroundsGL; -class DisplayLink; +struct SharedSwapState +{ + struct SwapTexture + { + GLuint texture; + unsigned int width; + unsigned int height; + uint64_t swapId; + }; + + SwapTexture textures[3]; + + // This code path is not going to be used by Chrome so we take the liberty + // to use pthreads directly instead of using mutexes and condition variables + // via the Platform API. + pthread_mutex_t mutex; + // The following members should be accessed only when holding the mutex + // (or doing construction / destruction) + SwapTexture *beingRendered; + SwapTexture *lastRendered; + SwapTexture *beingPresented; +}; class WindowSurfaceCGL : public SurfaceGL { public: - WindowSurfaceCGL(RendererGL *renderer, CALayer *layer, const FunctionsGL *functions); + WindowSurfaceCGL(RendererGL *renderer, + CALayer *layer, + const FunctionsGL *functions, + CGLContextObj context); ~WindowSurfaceCGL() override; egl::Error initialize() override; @@ -38,7 +66,7 @@ class WindowSurfaceCGL : public SurfaceGL egl::Error swap() override; egl::Error postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) override; egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override; - egl::Error bindTexImage(EGLint buffer) override; + egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override; egl::Error releaseTexImage(EGLint buffer) override; void setSwapInterval(EGLint interval) override; @@ -51,27 +79,16 @@ class WindowSurfaceCGL : public SurfaceGL FramebufferImpl *createDefaultFramebuffer(const gl::Framebuffer::Data &data) override; private: - struct Surface - { - IOSurfaceRef ioSurface; - GLuint texture; - uint64_t lastPresentNanos; - }; - - void freeSurfaceData(Surface *surface); - egl::Error initializeSurfaceData(Surface *surface, int width, int height); + SwapLayer *mSwapLayer; + SharedSwapState mSwapState; + uint64_t mCurrentSwapId; CALayer *mLayer; + CGLContextObj mContext; const FunctionsGL *mFunctions; StateManagerGL *mStateManager; const WorkaroundsGL &mWorkarounds; - DisplayLink *mDisplayLink; - // CGL doesn't have a default framebuffer, we instead render to an IOSurface - // that will be set as the content of the CALayer which is our native window. - // We use two IOSurfaces to do double buffering. - Surface mSurfaces[2]; - int mCurrentSurface; GLuint mFramebuffer; GLuint mDSRenderbuffer; }; diff --git a/gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm b/gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm index 04cd88e3fe..3b54d91b45 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm +++ b/gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm @@ -9,8 +9,6 @@ #include "libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h" #import -#include -#include #include #import @@ -20,146 +18,151 @@ #include "libANGLE/renderer/gl/RendererGL.h" #include "libANGLE/renderer/gl/StateManagerGL.h" -#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 - -#include - -namespace +@interface SwapLayer : CAOpenGLLayer { + CGLContextObj mDisplayContext; -// All time computations in the file are done in nanoseconds but both the -// current time and the screen's present time are given as "mach time". -// The first thing we will do after receiving a mach time is convert it. -uint64_t MachTimeToNanoseconds(uint64_t machTime) -{ - static mach_timebase_info_data_t timebaseInfo; + bool initialized; + rx::SharedSwapState *mSwapState; + const rx::FunctionsGL *mFunctions; - if (timebaseInfo.denom == 0) + GLuint mReadFramebuffer; +} +- (id)initWithSharedState:(rx::SharedSwapState *)swapState + withContext:(CGLContextObj)displayContext + withFunctions:(const rx::FunctionsGL *)functions; +@end + +@implementation SwapLayer +- (id)initWithSharedState:(rx::SharedSwapState *)swapState + withContext:(CGLContextObj)displayContext + withFunctions:(const rx::FunctionsGL *)functions { - mach_timebase_info(&timebaseInfo); - } - - return (machTime * timebaseInfo.numer) / timebaseInfo.denom; -} - -uint64_t NowInNanoseconds() -{ - return MachTimeToNanoseconds(mach_absolute_time()); -} - -} - -namespace rx -{ - -// A wrapper around CoreVideo's DisplayLink. It polls the screen vsync time, -// and allows computing when is the next present time after a given time point. -class DisplayLink -{ - public: - DisplayLink() : mDisplayLink(nullptr), mHostTimeRemainderNanos(0), mPresentIntervalNanos(0) {} - - ~DisplayLink() - { - if (mDisplayLink != nullptr) + self = [super init]; + if (self != nil) { - stop(); - CVDisplayLinkRelease(mDisplayLink); - mDisplayLink = nullptr; + self.asynchronous = YES; + mDisplayContext = displayContext; + + initialized = false; + mSwapState = swapState; + mFunctions = functions; + + [self setFrame:CGRectMake(0, 0, mSwapState->textures[0].width, + mSwapState->textures[0].height)]; } + return self; } - void start() + - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { - CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink); - ASSERT(mDisplayLink != nullptr); - CVDisplayLinkSetOutputCallback(mDisplayLink, &Callback, this); - CVDisplayLinkStart(mDisplayLink); + CGLPixelFormatAttribute attribs[] = { + kCGLPFADisplayMask, static_cast(mask), kCGLPFAOpenGLProfile, + static_cast(kCGLOGLPVersion_3_2_Core), + static_cast(0)}; + + CGLPixelFormatObj pixelFormat = nullptr; + GLint numFormats = 0; + CGLChoosePixelFormat(attribs, &pixelFormat, &numFormats); + + return pixelFormat; } - void stop() + - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat { - ASSERT(mDisplayLink != nullptr); - CVDisplayLinkStop(mDisplayLink); + CGLContextObj context = nullptr; + CGLCreateContext(pixelFormat, mDisplayContext, &context); + return context; } - uint64_t nextPresentAfter(uint64_t time) + - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext + pixelFormat:(CGLPixelFormatObj)pixelFormat + forLayerTime:(CFTimeInterval)timeInterval + displayTime:(const CVTimeStamp *)timeStamp { - // Load the two variables locally to avoid them changing in the middle of the computation. - uint64_t presentInterval = mPresentIntervalNanos; - uint64_t remainder = mHostTimeRemainderNanos; + BOOL result = NO; - if (presentInterval == 0) + pthread_mutex_lock(&mSwapState->mutex); { - return time; + if (mSwapState->lastRendered->swapId > mSwapState->beingPresented->swapId) + { + std::swap(mSwapState->lastRendered, mSwapState->beingPresented); + result = YES; + } + } + pthread_mutex_unlock(&mSwapState->mutex); + + return result; + } + + - (void)drawInCGLContext:(CGLContextObj)glContext + pixelFormat:(CGLPixelFormatObj)pixelFormat + forLayerTime:(CFTimeInterval)timeInterval + displayTime:(const CVTimeStamp *)timeStamp + { + CGLSetCurrentContext(glContext); + if (!initialized) + { + initialized = true; + + mFunctions->genFramebuffers(1, &mReadFramebuffer); } - uint64_t nextSwapNumber = (time - remainder) / presentInterval + 1; - return nextSwapNumber * presentInterval + remainder; + const auto &texture = *mSwapState->beingPresented; + if ([self frame].size.width != texture.width || [self frame].size.height != texture.height) + { + [self setFrame:CGRectMake(0, 0, texture.width, texture.height)]; + + // Without this, the OSX compositor / window system doesn't see the resize. + [self setNeedsDisplay]; + } + + // TODO(cwallez) support 2.1 contexts too that don't have blitFramebuffer nor the + // GL_DRAW_FRAMEBUFFER_BINDING query + GLint drawFBO; + mFunctions->getIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawFBO); + + mFunctions->bindFramebuffer(GL_FRAMEBUFFER, mReadFramebuffer); + mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + texture.texture, 0); + + mFunctions->bindFramebuffer(GL_READ_FRAMEBUFFER, mReadFramebuffer); + mFunctions->bindFramebuffer(GL_DRAW_FRAMEBUFFER, drawFBO); + mFunctions->blitFramebuffer(0, 0, texture.width, texture.height, 0, 0, texture.width, + texture.height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + // Call the super method to flush the context + [super drawInCGLContext:glContext + pixelFormat:pixelFormat + forLayerTime:timeInterval + displayTime:timeStamp]; } + @end - uint64_t getPresentInterval() const { return mPresentIntervalNanos; } - - private: - static CVReturn Callback(CVDisplayLinkRef displayLink, - const CVTimeStamp *now, - const CVTimeStamp *outputTime, - CVOptionFlags flagsIn, - CVOptionFlags *flagsOut, - void *userData) + namespace rx { - DisplayLink *link = reinterpret_cast(userData); - link->tick(*outputTime); - return kCVReturnSuccess; - } - void tick(const CVTimeStamp &outputTime) - { - // This is called from a special high priority thread so by setting - // both member variables here we have a data race. However if we assume - // the present interval doesn't change, the data race disappears after - // the first tick. - // In addition computations using these member variables are made to - // produce a sensible result if one of the two members has the default - // value of 0. - uint64_t hostTimeNanos = MachTimeToNanoseconds(outputTime.hostTime); - - uint64_t numerator = outputTime.videoRefreshPeriod; - uint64_t denominator = outputTime.videoTimeScale; - uint64_t presentIntervalNanos = (1000000000 * numerator) / denominator; - - mHostTimeRemainderNanos = hostTimeNanos % presentIntervalNanos; - mPresentIntervalNanos = presentIntervalNanos; - } - - CVDisplayLinkRef mDisplayLink; - - uint64_t mHostTimeRemainderNanos; - uint64_t mPresentIntervalNanos; -}; - -WindowSurfaceCGL::WindowSurfaceCGL(RendererGL *renderer, - CALayer *layer, - const FunctionsGL *functions) - : SurfaceGL(renderer), - mLayer(layer), - mFunctions(functions), - mStateManager(renderer->getStateManager()), - mWorkarounds(renderer->getWorkarounds()), - mDisplayLink(nullptr), - mCurrentSurface(0), - mFramebuffer(0), - mDSRenderbuffer(0) + WindowSurfaceCGL::WindowSurfaceCGL(RendererGL *renderer, + CALayer *layer, + const FunctionsGL *functions, + CGLContextObj context) + : SurfaceGL(renderer), + mSwapLayer(nil), + mCurrentSwapId(0), + mLayer(layer), + mContext(context), + mFunctions(functions), + mStateManager(renderer->getStateManager()), + mWorkarounds(renderer->getWorkarounds()), + mFramebuffer(0), + mDSRenderbuffer(0) { - for (auto &surface : mSurfaces) - { - surface.texture = 0; - surface.ioSurface = nil; - } + pthread_mutex_init(&mSwapState.mutex, nullptr); } WindowSurfaceCGL::~WindowSurfaceCGL() { + pthread_mutex_destroy(&mSwapState.mutex); if (mFramebuffer != 0) { mFunctions->deleteFramebuffers(1, &mFramebuffer); @@ -172,14 +175,20 @@ WindowSurfaceCGL::~WindowSurfaceCGL() mDSRenderbuffer = 0; } - for (auto &surface : mSurfaces) + if (mSwapLayer != nil) { - freeSurfaceData(&surface); + [mSwapLayer removeFromSuperlayer]; + [mSwapLayer release]; + mSwapLayer = nil; } - if (mDisplayLink != nullptr) + for (size_t i = 0; i < ArraySize(mSwapState.textures); ++i) { - SafeDelete(mDisplayLink); + if (mSwapState.textures[i].texture != 0) + { + mFunctions->deleteTextures(1, &mSwapState.textures[i].texture); + mSwapState.textures[i].texture = 0; + } } } @@ -188,11 +197,24 @@ egl::Error WindowSurfaceCGL::initialize() unsigned width = getWidth(); unsigned height = getHeight(); - for (auto &surface : mSurfaces) + for (size_t i = 0; i < ArraySize(mSwapState.textures); ++i) { - surface.lastPresentNanos = 0; - initializeSurfaceData(&surface, width, height); + mFunctions->genTextures(1, &mSwapState.textures[i].texture); + mStateManager->bindTexture(GL_TEXTURE_2D, mSwapState.textures[i].texture); + mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); + mSwapState.textures[i].width = width; + mSwapState.textures[i].height = height; + mSwapState.textures[i].swapId = 0; } + mSwapState.beingRendered = &mSwapState.textures[0]; + mSwapState.lastRendered = &mSwapState.textures[1]; + mSwapState.beingPresented = &mSwapState.textures[2]; + + mSwapLayer = [[SwapLayer alloc] initWithSharedState:&mSwapState + withContext:mContext + withFunctions:mFunctions]; + [mLayer addSublayer:mSwapLayer]; mFunctions->genRenderbuffers(1, &mDSRenderbuffer); mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mDSRenderbuffer); @@ -200,92 +222,50 @@ egl::Error WindowSurfaceCGL::initialize() mFunctions->genFramebuffers(1, &mFramebuffer); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); - mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, - mSurfaces[0].texture, 0); + mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + mSwapState.beingRendered->texture, 0); mFunctions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDSRenderbuffer); - mDisplayLink = new DisplayLink; - mDisplayLink->start(); - return egl::Error(EGL_SUCCESS); } egl::Error WindowSurfaceCGL::makeCurrent() { - // TODO(cwallez) if it is the first makeCurrent set the viewport and scissor? return egl::Error(EGL_SUCCESS); } egl::Error WindowSurfaceCGL::swap() { - // Because we are rendering to IOSurfaces we have to implement swapBuffers ourselves - // (contrary to GLX, WGL or EGL) and have to send the IOSurfaces for presentation at the - // right time to avoid causing tearing or flickering. Likewise we must make sure we do - // not render to an IOSurface that is still being presented. - // ANGLE standalone cannot post function to be executed at a later time so we can only - // implement basic synchronization with the present time by sleeping. - uint64_t presentInterval = mDisplayLink->getPresentInterval(); - uint64_t now = NowInNanoseconds(); + mFunctions->flush(); + mSwapState.beingRendered->swapId = ++mCurrentSwapId; + pthread_mutex_lock(&mSwapState.mutex); { - Surface &surface = mSurfaces[mCurrentSurface]; - // A flush is needed for the IOSurface to get the result of the GL operations - // as specified in the documentation of CGLTexImageIOSurface2D - mFunctions->flush(); + std::swap(mSwapState.beingRendered, mSwapState.lastRendered); + } + pthread_mutex_unlock(&mSwapState.mutex); - // We can only send the IOSurface to the CALayer during a present window - // that is in the middle of two vsyncs, otherwise flickering can happen. - { - // The present window was determined empirically. - static const double kPresentWindowMin = 0.3; - static const double kPresentWindowMax = 0.7; + unsigned width = getWidth(); + unsigned height = getHeight(); + auto &texture = *mSwapState.beingRendered; - uint64_t nextPresent = mDisplayLink->nextPresentAfter(now); - uint64_t presentWindowMin = nextPresent - presentInterval * (1.0 - kPresentWindowMin); - uint64_t presentWindowMax = nextPresent - presentInterval * (1.0 - kPresentWindowMax); - if (now <= presentWindowMin) - { - usleep((presentWindowMin - now) / 1000); - } - else if (now >= presentWindowMax) - { - uint64_t presentTarget = nextPresent + presentInterval * kPresentWindowMin; - usleep((presentTarget - now) / 1000); - } - } + if (texture.width != width || texture.height != texture.height) + { + mStateManager->bindTexture(GL_TEXTURE_2D, texture.texture); + mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); - // Put the IOSurface as the content of the layer - [CATransaction begin]; - [mLayer setContents:(id)surface.ioSurface]; - [CATransaction commit]; + mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mDSRenderbuffer); + mFunctions->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); - surface.lastPresentNanos = NowInNanoseconds(); + texture.width = width; + texture.height = height; } - mCurrentSurface = (mCurrentSurface + 1) % ArraySize(mSurfaces); - - { - Surface &surface = mSurfaces[mCurrentSurface]; - - { - // We need to wait a bit after a swap before rendering to the IOSurface again - // otherwise with a small desync between clocks could cause a flickering. - static const double kDrawWindowStart = 0.05; - - uint64_t timePresentFinishes = mDisplayLink->nextPresentAfter(surface.lastPresentNanos); - timePresentFinishes += presentInterval * kDrawWindowStart; - - if (now < timePresentFinishes) - { - usleep((timePresentFinishes - now) / 1000); - } - } - - mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); - mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_RECTANGLE_ARB, surface.texture, 0); - } + mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + mSwapState.beingRendered->texture, 0); return egl::Error(EGL_SUCCESS); } @@ -302,7 +282,7 @@ egl::Error WindowSurfaceCGL::querySurfacePointerANGLE(EGLint attribute, void **v return egl::Error(EGL_SUCCESS); } -egl::Error WindowSurfaceCGL::bindTexImage(EGLint buffer) +egl::Error WindowSurfaceCGL::bindTexImage(gl::Texture *texture, EGLint buffer) { UNIMPLEMENTED(); return egl::Error(EGL_SUCCESS); @@ -316,7 +296,7 @@ egl::Error WindowSurfaceCGL::releaseTexImage(EGLint buffer) void WindowSurfaceCGL::setSwapInterval(EGLint interval) { - // TODO(cwallez) investigate the need for swapIntervals other than 1 + // TODO(cwallez) investigate implementing swap intervals other than 0 } EGLint WindowSurfaceCGL::getWidth() const @@ -346,53 +326,4 @@ FramebufferImpl *WindowSurfaceCGL::createDefaultFramebuffer(const gl::Framebuffe return new FramebufferGL(mFramebuffer, data, mFunctions, mWorkarounds, mStateManager); } -void WindowSurfaceCGL::freeSurfaceData(Surface *surface) -{ - if (surface->texture != 0) - { - mFunctions->deleteTextures(1, &surface->texture); - surface->texture = 0; - } - if (surface->ioSurface != nil) - { - CFRelease(surface->ioSurface); - surface->ioSurface = nil; - } -} - -egl::Error WindowSurfaceCGL::initializeSurfaceData(Surface *surface, int width, int height) -{ - CFDictionaryRef ioSurfaceOptions = nil; - { - unsigned pixelFormat = 'BGRA'; - const unsigned kBytesPerElement = 4; - - NSDictionary *options = @{ - (id) kIOSurfaceWidth : @(width), - (id) kIOSurfaceHeight : @(height), - (id) kIOSurfacePixelFormat : @(pixelFormat), - (id) kIOSurfaceBytesPerElement : @(kBytesPerElement), - }; - ioSurfaceOptions = reinterpret_cast(options); - } - - surface->ioSurface = IOSurfaceCreate(ioSurfaceOptions); - - mFunctions->genTextures(1, &surface->texture); - mFunctions->bindTexture(GL_TEXTURE_RECTANGLE_ARB, surface->texture); - - CGLError error = - CGLTexImageIOSurface2D(CGLGetCurrentContext(), GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, width, - height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, surface->ioSurface, 0); - - if (error != kCGLNoError) - { - std::string errorMessage = - "Could not create the IOSurfaces: " + std::string(CGLErrorString(error)); - return egl::Error(EGL_BAD_NATIVE_WINDOW, errorMessage.c_str()); - } - - return egl::Error(EGL_SUCCESS); -} - } diff --git a/gfx/angle/src/libANGLE/renderer/gl/formatutilsgl.cpp b/gfx/angle/src/libANGLE/renderer/gl/formatutilsgl.cpp index b2f24f2af9..8f6b3215c1 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/formatutilsgl.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/formatutilsgl.cpp @@ -35,6 +35,7 @@ InternalFormat::InternalFormat() { } +// supported = version || vertexExt static inline SupportRequirement VersionOrExts(GLuint major, GLuint minor, const std::string &versionExt) { SupportRequirement requirement; @@ -44,15 +45,21 @@ static inline SupportRequirement VersionOrExts(GLuint major, GLuint minor, const return requirement; } -static inline SupportRequirement VersionAndExts(GLuint major, GLuint minor, const std::string &requiredExt) +// supported = (version || vertexExt) && requiredExt +static inline SupportRequirement VersionOrExtsAndExts(GLuint major, + GLuint minor, + const std::string &versionExt, + const std::string &requiredExt) { SupportRequirement requirement; requirement.version.major = major; requirement.version.minor = minor; + angle::SplitStringAlongWhitespace(versionExt, &requirement.versionExtensions); angle::SplitStringAlongWhitespace(requiredExt, &requirement.requiredExtensions); return requirement; } +// supported = version static inline SupportRequirement VersionOnly(GLuint major, GLuint minor) { SupportRequirement requirement; @@ -61,6 +68,7 @@ static inline SupportRequirement VersionOnly(GLuint major, GLuint minor) return requirement; } +// supported = ext static inline SupportRequirement ExtsOnly(const std::string &ext) { SupportRequirement requirement; @@ -70,6 +78,7 @@ static inline SupportRequirement ExtsOnly(const std::string &ext) return requirement; } +// supported = true static inline SupportRequirement Always() { SupportRequirement requirement; @@ -78,6 +87,7 @@ static inline SupportRequirement Always() return requirement; } +// supported = false static inline SupportRequirement Never() { SupportRequirement requirement; @@ -178,17 +188,17 @@ static InternalFormatInfoMap BuildInternalFormatInfoMap() InsertFormatMapping(&map, GL_BGRA_EXT, VersionOrExts(1, 2, "GL_EXT_bgra"), Always(), VersionOrExts(1, 2, "GL_EXT_bgra"), ExtsOnly("GL_EXT_texture_format_BGRA8888"), Always(), ExtsOnly("GL_EXT_texture_format_BGRA8888")); // Floating point formats - // | Format | OpenGL texture support | Filter | OpenGL render support | OpenGL ES texture support | Filter | OpenGL ES render support | - InsertFormatMapping(&map, GL_R11F_G11F_B10F, VersionOrExts(3, 0, "GL_EXT_packed_float"), Always(), VersionOrExts(3, 0, "GL_EXT_packed_float GL_ARB_color_buffer_float"), VersionOnly(3, 0), Always(), VersionAndExts(3, 0, "GL_EXT_color_buffer_float") ); - InsertFormatMapping(&map, GL_RGB9_E5, VersionOrExts(3, 0, "GL_EXT_texture_shared_exponent"), Always(), VersionOrExts(3, 0, "GL_EXT_texture_shared_exponent GL_ARB_color_buffer_float"), VersionOnly(3, 0), Always(), VersionAndExts(3, 0, "GL_EXT_color_buffer_float") ); - InsertFormatMapping(&map, GL_R16F, VersionOrExts(3, 0, "GL_ARB_texture_rg ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_rg GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_half_float GL_EXT_texture_rg"), VersionOrExts(3, 0, "GL_OES_texture_half_float_linear"), VersionOrExts(3, 0, "GL_OES_texture_half_float GL_EXT_texture_rg")); - InsertFormatMapping(&map, GL_RG16F, VersionOrExts(3, 0, "GL_ARB_texture_rg ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_rg GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_half_float GL_EXT_texture_rg"), VersionOrExts(3, 0, "GL_OES_texture_half_float_linear"), VersionOrExts(3, 0, "GL_OES_texture_half_float GL_EXT_texture_rg")); - InsertFormatMapping(&map, GL_RGB16F, VersionOrExts(3, 0, "GL_ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_half_float"), VersionOrExts(3, 0, "GL_OES_texture_half_float_linear"), VersionOrExts(3, 0, "GL_OES_texture_half_float") ); - InsertFormatMapping(&map, GL_RGBA16F, VersionOrExts(3, 0, "GL_ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_half_float"), VersionOrExts(3, 0, "GL_OES_texture_half_float_linear"), VersionOrExts(3, 0, "GL_OES_texture_half_float") ); - InsertFormatMapping(&map, GL_R32F, VersionOrExts(3, 0, "GL_ARB_texture_rg GL_ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_rg GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_float GL_EXT_texture_rg"), VersionOrExts(3, 0, "GL_OES_texture_float_linear"), VersionOrExts(3, 0, "GL_OES_texture_float GL_EXT_texture_rg") ); - InsertFormatMapping(&map, GL_RG32F, VersionOrExts(3, 0, "GL_ARB_texture_rg GL_ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_rg GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_float GL_EXT_texture_rg"), VersionOrExts(3, 0, "GL_OES_texture_float_linear"), VersionOrExts(3, 0, "GL_OES_texture_float GL_EXT_texture_rg") ); - InsertFormatMapping(&map, GL_RGB32F, VersionOrExts(3, 0, "GL_ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_float"), VersionOrExts(3, 0, "GL_OES_texture_float_linear"), VersionOrExts(3, 0, "GL_OES_texture_float") ); - InsertFormatMapping(&map, GL_RGBA32F, VersionOrExts(3, 0, "GL_ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_float"), VersionOrExts(3, 0, "GL_OES_texture_float_linear"), VersionOrExts(3, 0, "GL_OES_texture_float") ); + // | Format | OpenGL texture support | Filter | OpenGL render support | OpenGL ES texture support | Filter | OpenGL ES render support | + InsertFormatMapping(&map, GL_R11F_G11F_B10F, VersionOrExts(3, 0, "GL_EXT_packed_float"), Always(), VersionOrExts(3, 0, "GL_EXT_packed_float GL_ARB_color_buffer_float"), VersionOnly(3, 0), Always(), ExtsOnly("GL_EXT_color_buffer_float") ); + InsertFormatMapping(&map, GL_RGB9_E5, VersionOrExts(3, 0, "GL_EXT_texture_shared_exponent"), Always(), VersionOrExts(3, 0, "GL_EXT_texture_shared_exponent GL_ARB_color_buffer_float"), VersionOnly(3, 0), Always(), Never() ); + InsertFormatMapping(&map, GL_R16F, VersionOrExts(3, 0, "GL_ARB_texture_rg ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_rg GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_half_float GL_EXT_texture_rg"), VersionOrExts(3, 0, "GL_OES_texture_half_float_linear"), VersionOrExtsAndExts(3, 0, "GL_EXT_texture_rg", "GL_EXT_color_buffer_half_float")); + InsertFormatMapping(&map, GL_RG16F, VersionOrExts(3, 0, "GL_ARB_texture_rg ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_rg GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_half_float GL_EXT_texture_rg"), VersionOrExts(3, 0, "GL_OES_texture_half_float_linear"), VersionOrExtsAndExts(3, 0, "GL_EXT_texture_rg", "GL_EXT_color_buffer_half_float")); + InsertFormatMapping(&map, GL_RGB16F, VersionOrExts(3, 0, "GL_ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_half_float"), VersionOrExts(3, 0, "GL_OES_texture_half_float_linear"), ExtsOnly("GL_EXT_color_buffer_half_float") ); + InsertFormatMapping(&map, GL_RGBA16F, VersionOrExts(3, 0, "GL_ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_half_float"), VersionOrExts(3, 0, "GL_OES_texture_half_float_linear"), ExtsOnly("GL_EXT_color_buffer_half_float") ); + InsertFormatMapping(&map, GL_R32F, VersionOrExts(3, 0, "GL_ARB_texture_rg GL_ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_rg GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_float GL_EXT_texture_rg"), VersionOrExts(3, 0, "GL_OES_texture_float_linear"), ExtsOnly("GL_EXT_color_buffer_float") ); + InsertFormatMapping(&map, GL_RG32F, VersionOrExts(3, 0, "GL_ARB_texture_rg GL_ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_rg GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_float GL_EXT_texture_rg"), VersionOrExts(3, 0, "GL_OES_texture_float_linear"), ExtsOnly("GL_EXT_color_buffer_float") ); + InsertFormatMapping(&map, GL_RGB32F, VersionOrExts(3, 0, "GL_ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_float"), VersionOrExts(3, 0, "GL_OES_texture_float_linear"), Never() ); + InsertFormatMapping(&map, GL_RGBA32F, VersionOrExts(3, 0, "GL_ARB_texture_float"), Always(), VersionOrExts(3, 0, "GL_ARB_texture_float GL_ARB_color_buffer_float"), VersionOrExts(3, 0, "GL_OES_texture_float"), VersionOrExts(3, 0, "GL_OES_texture_float_linear"), ExtsOnly("GL_EXT_color_buffer_float") ); // Depth stencil formats // | Format | OpenGL texture support | Filter | OpenGL render support | OpenGL ES texture support | Filter | OpenGL ES render support | @@ -329,10 +339,6 @@ static GLenum GetNativeInternalFormat(const FunctionsGL *functions, } } } - else if (functions->isAtLeastGLES(gl::Version(3, 0))) - { - result = sizedInternalFormat; - } return result; } diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp b/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp index 7ba9fd660c..4fda0c7be8 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp @@ -17,6 +17,7 @@ #include "libANGLE/Surface.h" #include "libANGLE/renderer/gl/glx/PbufferSurfaceGLX.h" #include "libANGLE/renderer/gl/glx/WindowSurfaceGLX.h" +#include "libANGLE/renderer/gl/renderergl_utils.h" namespace rx { @@ -41,9 +42,7 @@ class FunctionsGLGLX : public FunctionsGL { } - virtual ~FunctionsGLGLX() - { - } + ~FunctionsGLGLX() override {} private: void *loadProcAddress(const std::string &function) override @@ -57,6 +56,7 @@ class FunctionsGLGLX : public FunctionsGL DisplayGLX::DisplayGLX() : DisplayGL(), mFunctionsGL(nullptr), + mRequestedVisual(-1), mContextConfig(nullptr), mContext(nullptr), mDummyPbuffer(0), @@ -64,6 +64,8 @@ DisplayGLX::DisplayGLX() mIsMesa(false), mHasMultisample(false), mHasARBCreateContext(false), + mHasARBCreateContextProfile(false), + mHasEXTCreateContextES2Profile(false), mSwapControl(SwapControl::Absent), mMinSwapInterval(0), mMaxSwapInterval(0), @@ -80,6 +82,7 @@ egl::Error DisplayGLX::initialize(egl::Display *display) { mEGLDisplay = display; Display *xDisplay = display->getNativeDisplayId(); + const auto &attribMap = display->getAttributeMap(); // ANGLE_platform_angle allows the creation of a default display // using EGL_DEFAULT_DISPLAY (= nullptr). In this case just open @@ -102,6 +105,8 @@ egl::Error DisplayGLX::initialize(egl::Display *display) mHasMultisample = mGLX.minorVersion > 3 || mGLX.hasExtension("GLX_ARB_multisample"); mHasARBCreateContext = mGLX.hasExtension("GLX_ARB_create_context"); + mHasARBCreateContextProfile = mGLX.hasExtension("GLX_ARB_create_context_profile"); + mHasEXTCreateContextES2Profile = mGLX.hasExtension("GLX_EXT_create_context_es2_profile"); // Choose the swap_control extension to use, if any. // The EXT version is better as it allows glXSwapInterval to be called per @@ -137,14 +142,42 @@ egl::Error DisplayGLX::initialize(egl::Display *display) mMinSwapInterval = 1; } - // When glXMakeCurrent is called, the context and the surface must be - // compatible which in glX-speak means that their config have the same - // color buffer type, are both RGBA or ColorIndex, and their buffers have - // the same depth, if they exist. - // Since our whole EGL implementation is backed by only one GL context, this - // context must be compatible with all the GLXFBConfig corresponding to the - // EGLconfigs that we will be exposing. + if (attribMap.contains(EGL_X11_VISUAL_ID_ANGLE)) { + mRequestedVisual = attribMap.get(EGL_X11_VISUAL_ID_ANGLE, -1); + + // There is no direct way to get the GLXFBConfig matching an X11 visual ID + // so we have to iterate over all the GLXFBConfigs to find the right one. + int nConfigs; + int attribList[] = { + None, + }; + glx::FBConfig *allConfigs = mGLX.chooseFBConfig(attribList, &nConfigs); + + for (int i = 0; i < nConfigs; ++i) + { + if (getGLXFBConfigAttrib(allConfigs[i], GLX_VISUAL_ID) == mRequestedVisual) + { + mContextConfig = allConfigs[i]; + break; + } + } + XFree(allConfigs); + + if (mContextConfig == nullptr) + { + return egl::Error(EGL_NOT_INITIALIZED, "Invalid visual ID requested."); + } + } + else + { + // When glXMakeCurrent is called, the context and the surface must be + // compatible which in glX-speak means that their config have the same + // color buffer type, are both RGBA or ColorIndex, and their buffers have + // the same depth, if they exist. + // Since our whole EGL implementation is backed by only one GL context, this + // context must be compatible with all the GLXFBConfig corresponding to the + // EGLconfigs that we will be exposing. int nConfigs; int attribList[] = { @@ -168,7 +201,7 @@ egl::Error DisplayGLX::initialize(egl::Display *display) GLX_CONFIG_CAVEAT, GLX_NONE, None }; - glx::FBConfig* candidates = mGLX.chooseFBConfig(attribList, &nConfigs); + glx::FBConfig *candidates = mGLX.chooseFBConfig(attribList, &nConfigs); if (nConfigs == 0) { XFree(candidates); @@ -178,12 +211,26 @@ egl::Error DisplayGLX::initialize(egl::Display *display) XFree(candidates); } + const auto &eglAttributes = display->getAttributeMap(); if (mHasARBCreateContext) { - mContext = initializeContext(mContextConfig, display->getAttributeMap()); + egl::Error error = initializeContext(mContextConfig, eglAttributes, &mContext); + if (error.isError()) + { + return error; + } } else { + if (eglAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, + EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE) == + EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE) + { + return egl::Error(EGL_NOT_INITIALIZED, + "Cannot create an OpenGL ES platform on GLX without the " + "GLX_ARB_create_context extension."); + } + XVisualInfo visualTemplate; visualTemplate.visualid = getGLXFBConfigAttrib(mContextConfig, GLX_VISUAL_ID); @@ -198,11 +245,13 @@ egl::Error DisplayGLX::initialize(egl::Display *display) mContext = mGLX.createContext(&visuals[0], nullptr, true); XFree(visuals); + + if (!mContext) + { + return egl::Error(EGL_NOT_INITIALIZED, "Could not create GL context."); + } } - if (!mContext) - { - return egl::Error(EGL_NOT_INITIALIZED, "Could not create GL context."); - } + ASSERT(mContext); // FunctionsGL and DisplayGL need to make a few GL calls, for example to // query the version of the context so we need to make the context current. @@ -234,6 +283,18 @@ egl::Error DisplayGLX::initialize(egl::Display *display) mFunctionsGL = new FunctionsGLGLX(mGLX.getProc); mFunctionsGL->initialize(); + // TODO(cwallez, angleproject:1303) Disable the OpenGL ES backend on Linux NVIDIA and Intel as + // it has problems on our automated testing. An OpenGL ES backend might not trigger this test if + // there is no Desktop OpenGL support, but that's not the case in our automated testing. + VendorID vendor = GetVendorID(mFunctionsGL); + bool isOpenGLES = + eglAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE) == + EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE; + if (isOpenGLES && (vendor == VENDOR_ID_INTEL || vendor == VENDOR_ID_NVIDIA)) + { + return egl::Error(EGL_NOT_INITIALIZED, "Intel or NVIDIA OpenGL ES drivers are not supported."); + } + syncXCommands(); std::string rendererString = @@ -311,33 +372,50 @@ egl::Error DisplayGLX::getDevice(DeviceImpl **device) return egl::Error(EGL_BAD_DISPLAY); } -glx::Context DisplayGLX::initializeContext(glx::FBConfig config, - const egl::AttributeMap &eglAttributes) +egl::Error DisplayGLX::initializeContext(glx::FBConfig config, + const egl::AttributeMap &eglAttributes, + glx::Context *context) { - // Create a context of the requested version, if any. - EGLint requestedMajorVersion = - eglAttributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE); - EGLint requestedMinorVersion = - eglAttributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, EGL_DONT_CARE); - if (requestedMajorVersion != EGL_DONT_CARE && requestedMinorVersion != EGL_DONT_CARE) + int profileMask = 0; + + EGLint requestedDisplayType = + eglAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE); + if (requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE) { - std::vector contextAttributes; - contextAttributes.push_back(GLX_CONTEXT_MAJOR_VERSION_ARB); - contextAttributes.push_back(requestedMajorVersion); + if (!mHasEXTCreateContextES2Profile) + { + return egl::Error(EGL_NOT_INITIALIZED, + "Cannot create an OpenGL ES platform on GLX without the " + "GLX_EXT_create_context_es_profile extension."); + } - contextAttributes.push_back(GLX_CONTEXT_MINOR_VERSION_ARB); - contextAttributes.push_back(requestedMinorVersion); + ASSERT(mHasARBCreateContextProfile); + profileMask |= GLX_CONTEXT_ES2_PROFILE_BIT_EXT; + } - contextAttributes.push_back(None); - return createContextAttribs(config, contextAttributes); + // Create a context of the requested version, if any. + gl::Version requestedVersion( + eglAttributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE), + eglAttributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, EGL_DONT_CARE)); + if (static_cast(requestedVersion.major) != EGL_DONT_CARE && + static_cast(requestedVersion.minor) != EGL_DONT_CARE) + { + if (!(profileMask & GLX_CONTEXT_ES2_PROFILE_BIT_EXT) && + requestedVersion >= gl::Version(3, 2)) + { + profileMask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; + } + return createContextAttribs(config, requestedVersion, profileMask, context); } // It is commonly assumed that glXCreateContextAttrib will create a context // of the highest version possible but it is not specified in the spec and // is not true on the Mesa drivers. Instead we try to create a context per - // desktop GL version until we succeed, starting from newer version. + // GL version until we succeed, starting from newer version. When the default + // platform is selected, if no desktop context can be created, we fallback to + // an ES context. // clang-format off - const gl::Version desktopVersions[] = { + const gl::Version desktopVersionsFrom3_2[] = { gl::Version(4, 5), gl::Version(4, 4), gl::Version(4, 3), @@ -346,6 +424,8 @@ glx::Context DisplayGLX::initializeContext(glx::FBConfig config, gl::Version(4, 0), gl::Version(3, 3), gl::Version(3, 2), + }; + const gl::Version desktopVersionsPre3_2[] = { gl::Version(3, 1), gl::Version(3, 0), gl::Version(2, 0), @@ -356,36 +436,52 @@ glx::Context DisplayGLX::initializeContext(glx::FBConfig config, gl::Version(1, 1), gl::Version(1, 0), }; + const gl::Version esVersionsFrom2_0[] = { + gl::Version(3, 2), + gl::Version(3, 1), + gl::Version(3, 0), + gl::Version(2, 0), + }; // clang-format on - bool useProfile = mGLX.hasExtension("GLX_ARB_create_context_profile"); - for (size_t i = 0; i < ArraySize(desktopVersions); ++i) + // NOTE: below we return as soon as we're able to create a context so the + // "error" variable is EGL_SUCCESS when returned contrary to the common idiom + // of returning "error" when there is an actual error. + if (requestedDisplayType != EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE) { - const auto &version = desktopVersions[i]; - - std::vector contextAttributes; - contextAttributes.push_back(GLX_CONTEXT_MAJOR_VERSION_ARB); - contextAttributes.push_back(version.major); - - contextAttributes.push_back(GLX_CONTEXT_MINOR_VERSION_ARB); - contextAttributes.push_back(version.minor); - - if (useProfile && version >= gl::Version(3, 2)) + for (auto &version : desktopVersionsFrom3_2) { - contextAttributes.push_back(GLX_CONTEXT_PROFILE_MASK_ARB); - contextAttributes.push_back(GLX_CONTEXT_CORE_PROFILE_BIT_ARB); + egl::Error error = + createContextAttribs(config, version, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, context); + if (!error.isError()) + { + return error; + } } - - contextAttributes.push_back(None); - auto context = createContextAttribs(config, contextAttributes); - - if (context) + for (auto &version : desktopVersionsPre3_2) { - return context; + egl::Error error = createContextAttribs(config, version, 0, context); + if (!error.isError()) + { + return error; + } } } - return nullptr; + if (requestedDisplayType != EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + for (auto &version : esVersionsFrom2_0) + { + egl::Error error = + createContextAttribs(config, version, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, context); + if (!error.isError()) + { + return error; + } + } + } + + return egl::Error(EGL_NOT_INITIALIZED, "Could not create a backing OpenGL context."); } egl::ConfigSet DisplayGLX::generateConfigs() const @@ -435,6 +531,14 @@ egl::ConfigSet DisplayGLX::generateConfigs() const config.nativeVisualType = getGLXFBConfigAttrib(glxConfig, GLX_X_VISUAL_TYPE); config.nativeRenderable = EGL_TRUE; + // When a visual ID has been specified with EGL_ANGLE_x11_visual we should + // only return configs with this visual: it will maximize performance by avoid + // blits in the driver when showing the window on the screen. + if (mRequestedVisual != -1 && config.nativeVisualID != mRequestedVisual) + { + continue; + } + // Buffer sizes config.redSize = getGLXFBConfigAttrib(glxConfig, GLX_RED_SIZE); config.greenSize = getGLXFBConfigAttrib(glxConfig, GLX_GREEN_SIZE); @@ -601,6 +705,47 @@ std::string DisplayGLX::getVendorString() const return ""; } +egl::Error DisplayGLX::waitClient() const +{ + mGLX.waitGL(); + return egl::Error(EGL_SUCCESS); +} + +egl::Error DisplayGLX::waitNative(EGLint engine, + egl::Surface *drawSurface, + egl::Surface *readSurface) const +{ + // eglWaitNative is used to notice the driver of changes in X11 for the current surface, such as + // changes of the window size. We use this event to update the child window of WindowSurfaceGLX + // to match its parent window's size. + // Handling eglWaitNative this way helps the application control when resize happens. This is + // important because drivers have a tendency to clobber the back buffer when the windows are + // resized. See http://crbug.com/326995 + if (drawSurface != nullptr) + { + SurfaceGLX *glxDrawSurface = GetImplAs(drawSurface); + egl::Error error = glxDrawSurface->checkForResize(); + if (error.isError()) + { + return error; + } + } + + if (readSurface != drawSurface && readSurface != nullptr) + { + SurfaceGLX *glxReadSurface = GetImplAs(readSurface); + egl::Error error = glxReadSurface->checkForResize(); + if (error.isError()) + { + return error; + } + } + + // We still need to forward the resizing of the child window to the driver. + mGLX.waitX(); + return egl::Error(EGL_SUCCESS); +} + void DisplayGLX::syncXCommands() const { if (mUsesNewXDisplay) @@ -644,7 +789,7 @@ void DisplayGLX::setSwapInterval(glx::Drawable drawable, SwapControlData *data) { mGLX.swapIntervalMESA(data->targetSwapInterval); } - else if (mSwapControl == SwapControl::Mesa) + else if (mSwapControl == SwapControl::SGI) { mGLX.swapIntervalSGI(data->targetSwapInterval); } @@ -652,6 +797,11 @@ void DisplayGLX::setSwapInterval(glx::Drawable drawable, SwapControlData *data) } } +bool DisplayGLX::isValidWindowVisualId(unsigned long visualId) const +{ + return mRequestedVisual == -1 || static_cast(mRequestedVisual) == visualId; +} + const FunctionsGL *DisplayGLX::getFunctionsGL() const { return mFunctionsGL; @@ -660,6 +810,7 @@ const FunctionsGL *DisplayGLX::getFunctionsGL() const void DisplayGLX::generateExtensions(egl::DisplayExtensions *outExtensions) const { outExtensions->createContext = true; + outExtensions->createContextNoError = true; } void DisplayGLX::generateCaps(egl::Caps *outCaps) const @@ -675,15 +826,37 @@ int DisplayGLX::getGLXFBConfigAttrib(glx::FBConfig config, int attrib) const return result; } -glx::Context DisplayGLX::createContextAttribs(glx::FBConfig, const std::vector &attribs) const +egl::Error DisplayGLX::createContextAttribs(glx::FBConfig, + gl::Version version, + int profileMask, + glx::Context *context) const { + std::vector attribs; + attribs.push_back(GLX_CONTEXT_MAJOR_VERSION_ARB); + attribs.push_back(version.major); + + attribs.push_back(GLX_CONTEXT_MINOR_VERSION_ARB); + attribs.push_back(version.minor); + + if (profileMask != 0 && mHasARBCreateContextProfile) + { + attribs.push_back(GLX_CONTEXT_PROFILE_MASK_ARB); + attribs.push_back(profileMask); + } + + attribs.push_back(None); + // When creating a context with glXCreateContextAttribsARB, a variety of X11 errors can // be generated. To prevent these errors from crashing our process, we simply ignore // them and only look if GLXContext was created. auto oldErrorHandler = XSetErrorHandler(IgnoreX11Errors); - auto context = mGLX.createContextAttribsARB(mContextConfig, nullptr, True, attribs.data()); + *context = mGLX.createContextAttribsARB(mContextConfig, nullptr, True, attribs.data()); XSetErrorHandler(oldErrorHandler); - return context; + if (!*context) + { + return egl::Error(EGL_NOT_INITIALIZED, "Could not create GL context."); + } + return egl::Error(EGL_SUCCESS); } } diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.h b/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.h index 5fd6abb767..b3c1efd175 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.h +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.h @@ -67,6 +67,11 @@ class DisplayGLX : public DisplayGL std::string getVendorString() const override; + egl::Error waitClient() const override; + egl::Error waitNative(EGLint engine, + egl::Surface *drawSurface, + egl::Surface *readSurface) const override; + // Synchronizes with the X server, if the display has been opened by ANGLE. // Calling this is required at the end of every functions that does buffered // X calls (not for glX calls) otherwise there might be race conditions @@ -79,22 +84,30 @@ class DisplayGLX : public DisplayGL // acts as expected. void setSwapInterval(glx::Drawable drawable, SwapControlData *data); + bool isValidWindowVisualId(unsigned long visualId) const; + private: const FunctionsGL *getFunctionsGL() const override; - glx::Context initializeContext(glx::FBConfig config, const egl::AttributeMap &eglAttributes); + egl::Error initializeContext(glx::FBConfig config, + const egl::AttributeMap &eglAttributes, + glx::Context *context); void generateExtensions(egl::DisplayExtensions *outExtensions) const override; void generateCaps(egl::Caps *outCaps) const override; int getGLXFBConfigAttrib(glx::FBConfig config, int attrib) const; - glx::Context createContextAttribs(glx::FBConfig, const std::vector &attribs) const; + egl::Error createContextAttribs(glx::FBConfig, + gl::Version version, + int profileMask, + glx::Context *context) const; FunctionsGL *mFunctionsGL; //TODO(cwallez) yuck, change generateConfigs to be non-const or add a userdata member to egl::Config? mutable std::map configIdToGLXConfig; + EGLint mRequestedVisual; glx::FBConfig mContextConfig; glx::Context mContext; // A pbuffer the context is current on during ANGLE initialization @@ -104,6 +117,8 @@ class DisplayGLX : public DisplayGL bool mIsMesa; bool mHasMultisample; bool mHasARBCreateContext; + bool mHasARBCreateContextProfile; + bool mHasEXTCreateContextES2Profile; enum class SwapControl { diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.cpp b/gfx/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.cpp index 6e2e7f7191..15e882bf05 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.cpp @@ -22,7 +22,7 @@ PbufferSurfaceGLX::PbufferSurfaceGLX(RendererGL *renderer, const FunctionsGLX &glx, glx::Context context, glx::FBConfig fbConfig) - : SurfaceGL(renderer), + : SurfaceGLX(renderer), mWidth(width), mHeight(height), mLargest(largest), @@ -97,7 +97,7 @@ egl::Error PbufferSurfaceGLX::querySurfacePointerANGLE(EGLint attribute, void ** return egl::Error(EGL_SUCCESS); } -egl::Error PbufferSurfaceGLX::bindTexImage(EGLint buffer) +egl::Error PbufferSurfaceGLX::bindTexImage(gl::Texture *texture, EGLint buffer) { UNIMPLEMENTED(); return egl::Error(EGL_SUCCESS); @@ -134,4 +134,9 @@ EGLint PbufferSurfaceGLX::getSwapBehavior() const return EGL_BUFFER_PRESERVED; } +egl::Error PbufferSurfaceGLX::checkForResize() +{ + // The size of pbuffers never change + return egl::Error(EGL_SUCCESS); +} } diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.h b/gfx/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.h index f8d1bf76ea..67fb50a318 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.h +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.h @@ -9,16 +9,15 @@ #ifndef LIBANGLE_RENDERER_GL_GLX_PBUFFERSURFACEGLX_H_ #define LIBANGLE_RENDERER_GL_GLX_PBUFFERSURFACEGLX_H_ -#include "libANGLE/renderer/gl/SurfaceGL.h" #include "libANGLE/renderer/gl/glx/platform_glx.h" +#include "libANGLE/renderer/gl/glx/SurfaceGLX.h" namespace rx { -class DisplayGLX; class FunctionsGLX; -class PbufferSurfaceGLX : public SurfaceGL +class PbufferSurfaceGLX : public SurfaceGLX { public: PbufferSurfaceGLX(RendererGL *renderer, @@ -36,7 +35,7 @@ class PbufferSurfaceGLX : public SurfaceGL egl::Error swap() override; egl::Error postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) override; egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override; - egl::Error bindTexImage(EGLint buffer) override; + egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override; egl::Error releaseTexImage(EGLint buffer) override; void setSwapInterval(EGLint interval) override; @@ -46,6 +45,8 @@ class PbufferSurfaceGLX : public SurfaceGL EGLint isPostSubBufferSupported() const override; EGLint getSwapBehavior() const override; + egl::Error checkForResize() override; + private: unsigned mWidth; unsigned mHeight; diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/SurfaceGLX.h b/gfx/angle/src/libANGLE/renderer/gl/glx/SurfaceGLX.h new file mode 100644 index 0000000000..cccc01e503 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/SurfaceGLX.h @@ -0,0 +1,26 @@ +// +// Copyright (c) 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. +// + +// SurfaceGLX.h: common interface for GLX surfaces + +#ifndef LIBANGLE_RENDERER_GL_GLX_SURFACEGLX_H_ +#define LIBANGLE_RENDERER_GL_GLX_SURFACEGLX_H_ + +#include "libANGLE/renderer/gl/SurfaceGL.h" + +namespace rx +{ + +class SurfaceGLX : public SurfaceGL +{ + public: + SurfaceGLX(RendererGL *renderer) : SurfaceGL(renderer) {} + + virtual egl::Error checkForResize() = 0; +}; +} + +#endif // LIBANGLE_RENDERER_GL_GLX_SURFACEGLX_H_ diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.cpp b/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.cpp index c41982901b..0ef6aa6b9c 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.cpp @@ -16,6 +16,11 @@ namespace rx { +static int IgnoreX11Errors(Display *, XErrorEvent *) +{ + return 0; +} + WindowSurfaceGLX::WindowSurfaceGLX(const FunctionsGLX &glx, DisplayGLX *glxDisplay, RendererGL *renderer, @@ -23,7 +28,7 @@ WindowSurfaceGLX::WindowSurfaceGLX(const FunctionsGLX &glx, Display *display, glx::Context context, glx::FBConfig fbConfig) - : SurfaceGL(renderer), + : SurfaceGLX(renderer), mParent(window), mWindow(0), mDisplay(display), @@ -44,7 +49,14 @@ WindowSurfaceGLX::~WindowSurfaceGLX() if (mWindow) { + // When destroying the window, it may happen that the window has already been + // destroyed by the application (this happens in Chromium). There is no way to + // atomically check that a window exists and to destroy it so instead we call + // XDestroyWindow, ignoring any errors. + auto oldErrorHandler = XSetErrorHandler(IgnoreX11Errors); XDestroyWindow(mDisplay, mWindow); + XSync(mDisplay, False); + XSetErrorHandler(oldErrorHandler); } mGLXDisplay->syncXCommands(); @@ -52,12 +64,26 @@ WindowSurfaceGLX::~WindowSurfaceGLX() egl::Error WindowSurfaceGLX::initialize() { + // Check that the window's visual ID is valid, as part of the AMGLE_x11_visual + // extension. + { + XWindowAttributes windowAttributes; + XGetWindowAttributes(mDisplay, mParent, &windowAttributes); + unsigned long visualId = windowAttributes.visual->visualid; + + if (!mGLXDisplay->isValidWindowVisualId(visualId)) + { + return egl::Error(EGL_BAD_MATCH, + "The visual of native_window doesn't match the visual given with " + "ANGLE_X11_VISUAL_ID"); + } + } + // The visual of the X window, GLX window and GLX context must match, // however we received a user-created window that can have any visual // and wouldn't work with our GLX context. To work in all cases, we // create a child window with the right visual that covers all of its // parent. - XVisualInfo *visualInfo = mGLX.getVisualFromFBConfig(mFBConfig); if (!visualInfo) { @@ -116,27 +142,17 @@ egl::Error WindowSurfaceGLX::makeCurrent() egl::Error WindowSurfaceGLX::swap() { - //TODO(cwallez) set up our own error handler to see if the call failed - unsigned int newParentWidth, newParentHeight; - if (!getWindowDimensions(mParent, &newParentWidth, &newParentHeight)) - { - // TODO(cwallez) What error type here? - return egl::Error(EGL_BAD_CURRENT_SURFACE, "Failed to retrieve the size of the parent window."); - } - - if (mParentWidth != newParentWidth || mParentHeight != newParentHeight) - { - mParentWidth = newParentWidth; - mParentHeight = newParentHeight; - - mGLX.waitGL(); - XResizeWindow(mDisplay, mWindow, mParentWidth, mParentHeight); - mGLX.waitX(); - } - + // We need to swap before resizing as some drivers clobber the back buffer + // when the window is resized. mGLXDisplay->setSwapInterval(mGLXWindow, &mSwapControl); mGLX.swapBuffers(mGLXWindow); + egl::Error error = checkForResize(); + if (error.isError()) + { + return error; + } + return egl::Error(EGL_SUCCESS); } @@ -152,7 +168,7 @@ egl::Error WindowSurfaceGLX::querySurfacePointerANGLE(EGLint attribute, void **v return egl::Error(EGL_SUCCESS); } -egl::Error WindowSurfaceGLX::bindTexImage(EGLint buffer) +egl::Error WindowSurfaceGLX::bindTexImage(gl::Texture *texture, EGLint buffer) { UNIMPLEMENTED(); return egl::Error(EGL_SUCCESS); @@ -192,6 +208,30 @@ EGLint WindowSurfaceGLX::getSwapBehavior() const return EGL_BUFFER_PRESERVED; } +egl::Error WindowSurfaceGLX::checkForResize() +{ + // TODO(cwallez) set up our own error handler to see if the call failed + unsigned int newParentWidth, newParentHeight; + if (!getWindowDimensions(mParent, &newParentWidth, &newParentHeight)) + { + return egl::Error(EGL_BAD_CURRENT_SURFACE, + "Failed to retrieve the size of the parent window."); + } + + if (mParentWidth != newParentWidth || mParentHeight != newParentHeight) + { + mParentWidth = newParentWidth; + mParentHeight = newParentHeight; + + mGLX.waitGL(); + XResizeWindow(mDisplay, mWindow, mParentWidth, mParentHeight); + mGLX.waitX(); + XSync(mDisplay, False); + } + + return egl::Error(EGL_SUCCESS); +} + bool WindowSurfaceGLX::getWindowDimensions(Window window, unsigned int *width, unsigned int *height) const { Window root; diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.h b/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.h index fff730fe74..6eaf308a67 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.h +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.h @@ -9,9 +9,9 @@ #ifndef LIBANGLE_RENDERER_GL_GLX_WINDOWSURFACEGLX_H_ #define LIBANGLE_RENDERER_GL_GLX_WINDOWSURFACEGLX_H_ -#include "libANGLE/renderer/gl/SurfaceGL.h" #include "libANGLE/renderer/gl/glx/DisplayGLX.h" #include "libANGLE/renderer/gl/glx/platform_glx.h" +#include "libANGLE/renderer/gl/glx/SurfaceGLX.h" namespace rx { @@ -19,7 +19,7 @@ namespace rx class DisplayGLX; class FunctionsGLX; -class WindowSurfaceGLX : public SurfaceGL +class WindowSurfaceGLX : public SurfaceGLX { public: WindowSurfaceGLX(const FunctionsGLX &glx, @@ -37,7 +37,7 @@ class WindowSurfaceGLX : public SurfaceGL egl::Error swap() override; egl::Error postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) override; egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override; - egl::Error bindTexImage(EGLint buffer) override; + egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override; egl::Error releaseTexImage(EGLint buffer) override; void setSwapInterval(EGLint interval) override; @@ -47,6 +47,8 @@ class WindowSurfaceGLX : public SurfaceGL EGLint isPostSubBufferSupported() const override; EGLint getSwapBehavior() const override; + egl::Error checkForResize() override; + private: bool getWindowDimensions(Window window, unsigned int *width, unsigned int *height) const; diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/platform_glx.h b/gfx/angle/src/libANGLE/renderer/gl/glx/platform_glx.h index a8d9cdec00..ee3a7ac551 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/platform_glx.h +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/platform_glx.h @@ -116,6 +116,9 @@ #define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 #define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 +// GLX_EXT_create_context_es2_profile +#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 + // GLX_EXT_texture_from_pixmap #define GLX_TEXTURE_1D_BIT_EXT 0x00000001 #define GLX_TEXTURE_2D_BIT_EXT 0x00000002 diff --git a/gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp b/gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp index fbb4fbf1dd..2921b02328 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp @@ -22,7 +22,7 @@ namespace rx { -static VendorID GetVendorID(const FunctionsGL *functions) +VendorID GetVendorID(const FunctionsGL *functions) { std::string nativeVendorString(reinterpret_cast(functions->getString(GL_VENDOR))); if (nativeVendorString.find("Intel") != std::string::npos) @@ -151,6 +151,13 @@ static gl::TypePrecision QueryTypePrecision(const FunctionsGL *functions, GLenum return precision; } +static GLint QueryQueryValue(const FunctionsGL *functions, GLenum target, GLenum name) +{ + GLint result; + functions->getQueryiv(target, name, &result); + return result; +} + static void LimitVersion(gl::Version *curVersion, const gl::Version &maxVersion) { if (*curVersion >= maxVersion) @@ -244,6 +251,9 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM } else { + // Framebuffer is required to have at least one drawbuffer even if the extension is not + // supported + caps->maxDrawBuffers = 1; LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); } @@ -447,8 +457,9 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); } - if (functions->isAtLeastGL(gl::Version(3, 0)) || functions->hasGLExtension("GL_ARB_ES3_compatibility") || - functions->isAtLeastGLES(gl::Version(3, 0))) + if (functions->isAtLeastGL(gl::Version(3, 0)) || + functions->hasGLExtension("GL_ARB_ES2_compatibility") || + functions->isAtLeastGLES(gl::Version(2, 0))) { caps->maxVaryingComponents = QuerySingleGLInt(functions, GL_MAX_VARYING_COMPONENTS); } @@ -548,6 +559,16 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); } + // TODO(geofflang): The gl-uniform-arrays WebGL conformance test struggles to complete on time + // if the max uniform vectors is too large. Artificially limit the maximum until the test is + // updated. + caps->maxVertexUniformVectors = std::min(1024u, caps->maxVertexUniformVectors); + caps->maxVertexUniformComponents = + std::min(caps->maxVertexUniformVectors / 4, caps->maxVertexUniformComponents); + caps->maxFragmentUniformVectors = std::min(1024u, caps->maxFragmentUniformVectors); + caps->maxFragmentUniformComponents = + std::min(caps->maxFragmentUniformVectors / 4, caps->maxFragmentUniformComponents); + // Extension support extensions->setTextureExtensionSupport(*textureCapsMap); extensions->elementIndexUint = functions->standard == STANDARD_GL_DESKTOP || @@ -598,9 +619,21 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM extensions->debugMarker = functions->isAtLeastGL(gl::Version(4, 3)) || functions->hasGLExtension("GL_KHR_debug") || functions->isAtLeastGLES(gl::Version(3, 2)) || functions->hasGLESExtension("GL_KHR_debug"); + if (functions->isAtLeastGL(gl::Version(3, 3)) || + functions->hasGLExtension("GL_ARB_timer_query") || + functions->hasGLESExtension("GL_EXT_disjoint_timer_query")) + { + extensions->disjointTimerQuery = true; + extensions->queryCounterBitsTimeElapsed = + QueryQueryValue(functions, GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS); + extensions->queryCounterBitsTimestamp = + QueryQueryValue(functions, GL_TIMESTAMP, GL_QUERY_COUNTER_BITS); + } // ANGLE emulates vertex array objects in its GL layer extensions->vertexArrayObject = true; + + extensions->noError = true; } void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workarounds) diff --git a/gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.h b/gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.h index b176e40651..fb7135a456 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.h +++ b/gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.h @@ -10,6 +10,7 @@ #ifndef LIBANGLE_RENDERER_GL_RENDERERGLUTILS_H_ #define LIBANGLE_RENDERER_GL_RENDERERGLUTILS_H_ +#include "libANGLE/angletypes.h" #include "libANGLE/renderer/gl/functionsgl_typedefs.h" #include @@ -28,6 +29,8 @@ namespace rx class FunctionsGL; struct WorkaroundsGL; +VendorID GetVendorID(const FunctionsGL *functions); + namespace nativegl_gl { diff --git a/gfx/angle/src/libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.cpp new file mode 100644 index 0000000000..bd705a89e6 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.cpp @@ -0,0 +1,550 @@ +// +// Copyright (c) 2015 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. +// + +// DXGISwapChainWindowSurfaceWGL.cpp: WGL implementation of egl::Surface for windows using a DXGI +// swapchain. + +#include "libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.h" + +#include "libANGLE/formatutils.h" +#include "libANGLE/renderer/gl/FramebufferGL.h" +#include "libANGLE/renderer/gl/TextureGL.h" +#include "libANGLE/renderer/gl/RendererGL.h" +#include "libANGLE/renderer/gl/StateManagerGL.h" +#include "libANGLE/renderer/gl/wgl/DisplayWGL.h" +#include "libANGLE/renderer/gl/wgl/FunctionsWGL.h" + +#include + +namespace rx +{ + +DXGISwapChainWindowSurfaceWGL::DXGISwapChainWindowSurfaceWGL(RendererGL *renderer, + EGLNativeWindowType window, + ID3D11Device *device, + HANDLE deviceHandle, + HGLRC wglContext, + HDC deviceContext, + const FunctionsGL *functionsGL, + const FunctionsWGL *functionsWGL, + EGLint orientation) + : SurfaceGL(renderer), + mWindow(window), + mStateManager(renderer->getStateManager()), + mWorkarounds(renderer->getWorkarounds()), + mFunctionsGL(functionsGL), + mFunctionsWGL(functionsWGL), + mDevice(device), + mDeviceHandle(deviceHandle), + mWGLDevice(deviceContext), + mWGLContext(wglContext), + mSwapChainFormat(DXGI_FORMAT_UNKNOWN), + mSwapChainFlags(0), + mDepthBufferFormat(GL_NONE), + mFirstSwap(true), + mSwapChain(nullptr), + mSwapChain1(nullptr), + mColorRenderbufferID(0), + mRenderbufferBufferHandle(nullptr), + mDepthRenderbufferID(0), + mFramebufferID(0), + mTextureID(0), + mTextureHandle(nullptr), + mWidth(0), + mHeight(0), + mSwapInterval(1), + mOrientation(orientation) +{ +} + +DXGISwapChainWindowSurfaceWGL::~DXGISwapChainWindowSurfaceWGL() +{ + if (mRenderbufferBufferHandle != nullptr) + { + mFunctionsWGL->dxUnlockObjectsNV(mDeviceHandle, 1, &mRenderbufferBufferHandle); + mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mRenderbufferBufferHandle); + } + + if (mColorRenderbufferID != 0) + { + mStateManager->deleteRenderbuffer(mColorRenderbufferID); + mColorRenderbufferID = 0; + } + + if (mDepthRenderbufferID != 0) + { + mStateManager->deleteRenderbuffer(mDepthRenderbufferID); + mDepthRenderbufferID = 0; + } + + SafeRelease(mSwapChain); + SafeRelease(mSwapChain1); +} + +egl::Error DXGISwapChainWindowSurfaceWGL::initialize() +{ + if (mOrientation != EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE) + { + // TODO(geofflang): Support the orientation extensions fully. Currently only inverting Y is + // supported. To support all orientations, an intermediate framebuffer will be needed with + // a blit before swap. + return egl::Error(EGL_BAD_ATTRIBUTE, + "DXGISwapChainWindowSurfaceWGL requires an orientation of " + "EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE."); + } + + RECT rect; + if (!GetClientRect(mWindow, &rect)) + { + return egl::Error(EGL_BAD_NATIVE_WINDOW, "Failed to query the window size."); + } + mWidth = rect.right - rect.left; + mHeight = rect.bottom - rect.top; + + mSwapChainFormat = DXGI_FORMAT_R8G8B8A8_UNORM; + mSwapChainFlags = 0; + mDepthBufferFormat = GL_DEPTH24_STENCIL8; + + mFunctionsGL->genFramebuffers(1, &mFramebufferID); + mFunctionsGL->genRenderbuffers(1, &mColorRenderbufferID); + mFunctionsGL->genRenderbuffers(1, &mDepthRenderbufferID); + + return createSwapChain(); +} + +egl::Error DXGISwapChainWindowSurfaceWGL::makeCurrent() +{ + if (!mFunctionsWGL->makeCurrent(mWGLDevice, mWGLContext)) + { + // TODO: What error type here? + return egl::Error(EGL_CONTEXT_LOST, "Failed to make the WGL context current."); + } + + return egl::Error(EGL_SUCCESS); +} + +egl::Error DXGISwapChainWindowSurfaceWGL::swap() +{ + mFunctionsGL->flush(); + + egl::Error error = setObjectsLocked(false); + if (error.isError()) + { + return error; + } + + HRESULT result = mSwapChain->Present(mSwapInterval, 0); + mFirstSwap = false; + + error = setObjectsLocked(true); + if (error.isError()) + { + return error; + } + + if (FAILED(result)) + { + return egl::Error(EGL_BAD_ALLOC, "Failed to present swap chain, result: 0x%X", result); + } + + return checkForResize(); +} + +egl::Error DXGISwapChainWindowSurfaceWGL::postSubBuffer(EGLint x, + EGLint y, + EGLint width, + EGLint height) +{ + ASSERT(mSwapChain1 != nullptr); + + mFunctionsGL->flush(); + + egl::Error error = setObjectsLocked(false); + if (error.isError()) + { + return error; + } + + HRESULT result = S_OK; + if (mFirstSwap) + { + result = mSwapChain1->Present(mSwapInterval, 0); + mFirstSwap = false; + } + else + { + RECT rect = {static_cast(x), static_cast(mHeight - y - height), + static_cast(x + width), static_cast(mHeight - y)}; + DXGI_PRESENT_PARAMETERS params = {1, &rect, nullptr, nullptr}; + result = mSwapChain1->Present1(mSwapInterval, 0, ¶ms); + } + + error = setObjectsLocked(true); + if (error.isError()) + { + return error; + } + + if (FAILED(result)) + { + return egl::Error(EGL_BAD_ALLOC, "Failed to present swap chain, result: 0x%X", result); + } + + return checkForResize(); +} + +egl::Error DXGISwapChainWindowSurfaceWGL::querySurfacePointerANGLE(EGLint attribute, void **value) +{ + UNREACHABLE(); + return egl::Error(EGL_SUCCESS); +} + +egl::Error DXGISwapChainWindowSurfaceWGL::bindTexImage(gl::Texture *texture, EGLint buffer) +{ + ASSERT(mTextureHandle == nullptr); + + const TextureGL *textureGL = GetImplAs(texture); + GLuint textureID = textureGL->getTextureID(); + + ID3D11Texture2D *colorBuffer = nullptr; + HRESULT result = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), + reinterpret_cast(&colorBuffer)); + if (FAILED(result)) + { + return egl::Error(EGL_BAD_ALLOC, "Failed to query texture from swap chain, result: 0x%X", + result); + } + + mTextureHandle = mFunctionsWGL->dxRegisterObjectNV(mDeviceHandle, colorBuffer, textureID, + GL_TEXTURE_2D, WGL_ACCESS_READ_WRITE_NV); + SafeRelease(colorBuffer); + if (mTextureHandle == nullptr) + { + return egl::Error(EGL_BAD_ALLOC, "Failed to register D3D object, error: 0x%08x.", + HRESULT_CODE(GetLastError())); + } + + if (!mFunctionsWGL->dxLockObjectsNV(mDeviceHandle, 1, &mTextureHandle)) + { + mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mTextureHandle); + mTextureHandle = nullptr; + + return egl::Error(EGL_BAD_ALLOC, "Failed to lock D3D object, error: 0x%08x.", + HRESULT_CODE(GetLastError())); + } + + mTextureID = textureID; + + return egl::Error(EGL_SUCCESS); +} + +egl::Error DXGISwapChainWindowSurfaceWGL::releaseTexImage(EGLint buffer) +{ + ASSERT(mTextureHandle != nullptr); + + if (!mFunctionsWGL->dxUnlockObjectsNV(mDeviceHandle, 1, &mTextureHandle)) + { + return egl::Error(EGL_BAD_ALLOC, "Failed to unlock D3D object, error: 0x%08x.", + HRESULT_CODE(GetLastError())); + } + + if (!mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mTextureHandle)) + { + return egl::Error(EGL_BAD_ALLOC, "Failed to unregister D3D object, error: 0x%08x.", + HRESULT_CODE(GetLastError())); + } + + mTextureID = 0; + mTextureHandle = nullptr; + + return egl::Error(EGL_SUCCESS); +} + +void DXGISwapChainWindowSurfaceWGL::setSwapInterval(EGLint interval) +{ + mSwapInterval = interval; +} + +EGLint DXGISwapChainWindowSurfaceWGL::getWidth() const +{ + return static_cast(mWidth); +} + +EGLint DXGISwapChainWindowSurfaceWGL::getHeight() const +{ + return static_cast(mHeight); +} + +EGLint DXGISwapChainWindowSurfaceWGL::isPostSubBufferSupported() const +{ + return mSwapChain1 != nullptr; +} + +EGLint DXGISwapChainWindowSurfaceWGL::getSwapBehavior() const +{ + return EGL_BUFFER_DESTROYED; +} + +FramebufferImpl *DXGISwapChainWindowSurfaceWGL::createDefaultFramebuffer( + const gl::Framebuffer::Data &data) +{ + return new FramebufferGL(mFramebufferID, data, mFunctionsGL, mWorkarounds, mStateManager); +} + +egl::Error DXGISwapChainWindowSurfaceWGL::setObjectsLocked(bool locked) +{ + if (mRenderbufferBufferHandle == nullptr) + { + ASSERT(mTextureHandle == nullptr); + return egl::Error(EGL_SUCCESS); + } + + HANDLE resources[] = { + mRenderbufferBufferHandle, mTextureHandle, + }; + GLint count = (mTextureHandle != nullptr) ? 2 : 1; + + if (locked) + { + if (!mFunctionsWGL->dxLockObjectsNV(mDeviceHandle, count, resources)) + { + return egl::Error(EGL_BAD_ALLOC, "Failed to lock object, error: 0x%08x.", + HRESULT_CODE(GetLastError())); + } + } + else + { + if (!mFunctionsWGL->dxUnlockObjectsNV(mDeviceHandle, count, resources)) + { + return egl::Error(EGL_BAD_ALLOC, "Failed to lock object, error: 0x%08x.", + HRESULT_CODE(GetLastError())); + } + } + + return egl::Error(EGL_SUCCESS); +} + +egl::Error DXGISwapChainWindowSurfaceWGL::checkForResize() +{ + RECT rect; + if (!GetClientRect(mWindow, &rect)) + { + return egl::Error(EGL_BAD_NATIVE_WINDOW, "Failed to query the window size."); + } + + size_t newWidth = rect.right - rect.left; + size_t newHeight = rect.bottom - rect.top; + if (newWidth != mWidth || newHeight != mHeight) + { + mWidth = newWidth; + mHeight = newHeight; + + // TODO(geofflang): Handle resize by resizing the swap chain instead of re-creating it. + egl::Error error = createSwapChain(); + if (error.isError()) + { + return error; + } + } + + return egl::Error(EGL_SUCCESS); +} + +static IDXGIFactory *GetDXGIFactoryFromDevice(ID3D11Device *device) +{ + IDXGIDevice *dxgiDevice = nullptr; + HRESULT result = + device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast(&dxgiDevice)); + if (FAILED(result)) + { + return nullptr; + } + + IDXGIAdapter *dxgiAdapter = nullptr; + result = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast(&dxgiAdapter)); + SafeRelease(dxgiDevice); + if (FAILED(result)) + { + return nullptr; + } + + IDXGIFactory *dxgiFactory = nullptr; + result = + dxgiAdapter->GetParent(__uuidof(IDXGIFactory), reinterpret_cast(&dxgiFactory)); + SafeRelease(dxgiAdapter); + if (FAILED(result)) + { + return nullptr; + } + + return dxgiFactory; +} + +egl::Error DXGISwapChainWindowSurfaceWGL::createSwapChain() +{ + egl::Error error = setObjectsLocked(false); + if (error.isError()) + { + return error; + } + + if (mRenderbufferBufferHandle) + { + mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mRenderbufferBufferHandle); + mRenderbufferBufferHandle = nullptr; + } + + // If this surface is bound to a texture, unregister it. + bool hadBoundSurface = (mTextureHandle != nullptr); + if (hadBoundSurface) + { + mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mTextureHandle); + mTextureHandle = nullptr; + } + + IDXGIFactory *dxgiFactory = GetDXGIFactoryFromDevice(mDevice); + if (dxgiFactory == nullptr) + { + return egl::Error(EGL_BAD_NATIVE_WINDOW, "Failed to query the DXGIFactory."); + } + + IDXGIFactory2 *dxgiFactory2 = nullptr; + HRESULT result = dxgiFactory->QueryInterface(__uuidof(IDXGIFactory2), + reinterpret_cast(&dxgiFactory2)); + if (SUCCEEDED(result)) + { + ASSERT(dxgiFactory2 != nullptr); + + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0}; + swapChainDesc.BufferCount = 1; + swapChainDesc.Format = mSwapChainFormat; + swapChainDesc.Width = static_cast(mWidth); + swapChainDesc.Height = static_cast(mHeight); + swapChainDesc.Format = mSwapChainFormat; + swapChainDesc.Stereo = FALSE; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.BufferUsage = + DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_BACK_BUFFER; + swapChainDesc.BufferCount = 1; + swapChainDesc.Scaling = DXGI_SCALING_STRETCH; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL; + swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + swapChainDesc.Flags = mSwapChainFlags; + + result = dxgiFactory2->CreateSwapChainForHwnd(mDevice, mWindow, &swapChainDesc, nullptr, + nullptr, &mSwapChain1); + SafeRelease(dxgiFactory2); + SafeRelease(dxgiFactory); + if (FAILED(result)) + { + return egl::Error(EGL_BAD_ALLOC, "Failed to create swap chain for window, result: 0x%X", + result); + } + + mSwapChain = mSwapChain1; + mSwapChain->AddRef(); + } + else + { + DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; + swapChainDesc.BufferCount = 1; + swapChainDesc.BufferDesc.Format = mSwapChainFormat; + swapChainDesc.BufferDesc.Width = static_cast(mWidth); + swapChainDesc.BufferDesc.Height = static_cast(mHeight); + swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + swapChainDesc.BufferDesc.RefreshRate.Numerator = 0; + swapChainDesc.BufferDesc.RefreshRate.Denominator = 1; + swapChainDesc.BufferUsage = + DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_BACK_BUFFER; + swapChainDesc.Flags = mSwapChainFlags; + swapChainDesc.OutputWindow = mWindow; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.Windowed = TRUE; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + + result = dxgiFactory->CreateSwapChain(mDevice, &swapChainDesc, &mSwapChain); + SafeRelease(dxgiFactory); + if (FAILED(result)) + { + return egl::Error(EGL_BAD_ALLOC, "Failed to create swap chain for window, result: 0x%X", + result); + } + } + + ID3D11Texture2D *colorBuffer = nullptr; + result = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), + reinterpret_cast(&colorBuffer)); + if (FAILED(result)) + { + return egl::Error(EGL_BAD_ALLOC, "Failed to query texture from swap chain, result: 0x%X", + result); + } + + mFunctionsGL->genRenderbuffers(1, &mColorRenderbufferID); + mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mColorRenderbufferID); + mRenderbufferBufferHandle = + mFunctionsWGL->dxRegisterObjectNV(mDeviceHandle, colorBuffer, mColorRenderbufferID, + GL_RENDERBUFFER, WGL_ACCESS_READ_WRITE_NV); + SafeRelease(colorBuffer); + if (mRenderbufferBufferHandle == nullptr) + { + return egl::Error(EGL_BAD_ALLOC, "Failed to register D3D object, error: 0x%X.", + HRESULT_CODE(GetLastError())); + } + + // Rebind the surface to the texture if needed. + if (hadBoundSurface) + { + mTextureHandle = mFunctionsWGL->dxRegisterObjectNV(mDeviceHandle, colorBuffer, mTextureID, + GL_TEXTURE_2D, WGL_ACCESS_READ_WRITE_NV); + if (mTextureHandle == nullptr) + { + return egl::Error(EGL_BAD_ALLOC, "Failed to register D3D object, error: 0x%X.", + HRESULT_CODE(GetLastError())); + } + } + + error = setObjectsLocked(true); + if (error.isError()) + { + return error; + } + + ASSERT(mFramebufferID != 0); + mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); + mFunctionsGL->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, + mColorRenderbufferID); + + if (mDepthBufferFormat != GL_NONE) + { + ASSERT(mDepthRenderbufferID != 0); + mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mDepthRenderbufferID); + mFunctionsGL->renderbufferStorage(GL_RENDERBUFFER, mDepthBufferFormat, + static_cast(mWidth), + static_cast(mHeight)); + + const gl::InternalFormat &depthStencilFormatInfo = + gl::GetInternalFormatInfo(mDepthBufferFormat); + if (depthStencilFormatInfo.depthBits > 0) + { + mFunctionsGL->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, mDepthRenderbufferID); + } + if (depthStencilFormatInfo.stencilBits > 0) + { + mFunctionsGL->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, mDepthRenderbufferID); + } + } + + mFirstSwap = true; + + return egl::Error(EGL_SUCCESS); +} +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.h b/gfx/angle/src/libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.h new file mode 100644 index 0000000000..a4549c6e25 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.h @@ -0,0 +1,104 @@ +// +// Copyright (c) 2015 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. +// + +// DXGISwapChainWindowSurfaceWGL.h: WGL implementation of egl::Surface for windows using a DXGI +// swapchain. + +#ifndef LIBANGLE_RENDERER_GL_WGL_DXGISWAPCHAINSURFACEWGL_H_ +#define LIBANGLE_RENDERER_GL_WGL_DXGISWAPCHAINSURFACEWGL_H_ + +#include "libANGLE/renderer/gl/SurfaceGL.h" + +#include + +namespace rx +{ + +class FunctionsGL; +class FunctionsWGL; +class DisplayWGL; +class StateManagerGL; +struct WorkaroundsGL; + +class DXGISwapChainWindowSurfaceWGL : public SurfaceGL +{ + public: + DXGISwapChainWindowSurfaceWGL(RendererGL *renderer, + EGLNativeWindowType window, + ID3D11Device *device, + HANDLE deviceHandle, + HGLRC wglContext, + HDC deviceContext, + const FunctionsGL *functionsGL, + const FunctionsWGL *functionsWGL, + EGLint orientation); + ~DXGISwapChainWindowSurfaceWGL() override; + + egl::Error initialize() override; + egl::Error makeCurrent() override; + + egl::Error swap() override; + egl::Error postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) override; + egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override; + egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override; + egl::Error releaseTexImage(EGLint buffer) override; + void setSwapInterval(EGLint interval) override; + + EGLint getWidth() const override; + EGLint getHeight() const override; + + EGLint isPostSubBufferSupported() const override; + EGLint getSwapBehavior() const override; + + FramebufferImpl *createDefaultFramebuffer(const gl::Framebuffer::Data &data) override; + + private: + egl::Error setObjectsLocked(bool locked); + egl::Error checkForResize(); + + egl::Error createSwapChain(); + + EGLNativeWindowType mWindow; + + StateManagerGL *mStateManager; + const WorkaroundsGL &mWorkarounds; + const FunctionsGL *mFunctionsGL; + const FunctionsWGL *mFunctionsWGL; + + ID3D11Device *mDevice; + HANDLE mDeviceHandle; + + HDC mWGLDevice; + HGLRC mWGLContext; + + DXGI_FORMAT mSwapChainFormat; + UINT mSwapChainFlags; + GLenum mDepthBufferFormat; + + bool mFirstSwap; + IDXGISwapChain *mSwapChain; + IDXGISwapChain1 *mSwapChain1; + + GLuint mColorRenderbufferID; + HANDLE mRenderbufferBufferHandle; + + GLuint mDepthRenderbufferID; + + GLuint mFramebufferID; + + GLuint mTextureID; + HANDLE mTextureHandle; + + size_t mWidth; + size_t mHeight; + + EGLint mSwapInterval; + + EGLint mOrientation; +}; +} // namespace rx + +#endif // LIBANGLE_RENDERER_GL_WGL_DXGISWAPCHAINSURFACEWGL_H_ diff --git a/gfx/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp index 284b603891..79b82f3993 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp @@ -13,6 +13,7 @@ #include "libANGLE/Display.h" #include "libANGLE/Surface.h" #include "libANGLE/renderer/gl/renderergl_utils.h" +#include "libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.h" #include "libANGLE/renderer/gl/wgl/FunctionsWGL.h" #include "libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.h" #include "libANGLE/renderer/gl/wgl/WindowSurfaceWGL.h" @@ -36,9 +37,7 @@ class FunctionsGLWindows : public FunctionsGL ASSERT(mGetProcAddressWGL); } - virtual ~FunctionsGLWindows() - { - } + ~FunctionsGLWindows() override {} private: void *loadProcAddress(const std::string &function) override @@ -65,6 +64,11 @@ DisplayWGL::DisplayWGL() mDeviceContext(nullptr), mPixelFormat(0), mWGLContext(nullptr), + mUseDXGISwapChains(false), + mDxgiModule(nullptr), + mD3d11Module(nullptr), + mD3D11DeviceHandle(nullptr), + mD3D11Device(nullptr), mDisplay(nullptr) { } @@ -175,6 +179,18 @@ egl::Error DisplayWGL::initialize(egl::Display *display) ReleaseDC(dummyWindow, dummyDeviceContext); DestroyWindow(dummyWindow); + const egl::AttributeMap &displayAttributes = display->getAttributeMap(); + EGLint requestedDisplayType = + displayAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE); + if (requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE && + !mFunctionsWGL->hasExtension("WGL_EXT_create_context_es2_profile") && + !mFunctionsWGL->hasExtension("WGL_EXT_create_context_es_profile")) + { + return egl::Error(EGL_NOT_INITIALIZED, + "Cannot create an OpenGL ES platform on Windows without " + "the WGL_EXT_create_context_es(2)_profile extension."); + } + // Create the real intermediate context and windows mWindow = CreateWindowExA(0, reinterpret_cast(mWindowClass), @@ -230,14 +246,21 @@ egl::Error DisplayWGL::initialize(egl::Display *display) // TODO: handle robustness int mask = 0; - // Request core profile - mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; + + if (requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE) + { + mask |= WGL_CONTEXT_ES_PROFILE_BIT_EXT; + } + else + { + // Request core profile + mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; + } std::vector contextCreationAttributes; // Don't request a specific version unless the user wants one. WGL will return the highest version // that the driver supports if no version is requested. - const egl::AttributeMap &displayAttributes = display->getAttributeMap(); EGLint requestedMajorVersion = displayAttributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE); EGLint requestedMinorVersion = displayAttributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, EGL_DONT_CARE); if (requestedMajorVersion != EGL_DONT_CARE && requestedMinorVersion != EGL_DONT_CARE) @@ -248,6 +271,20 @@ egl::Error DisplayWGL::initialize(egl::Display *display) contextCreationAttributes.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); contextCreationAttributes.push_back(requestedMinorVersion); } + else + { + // the ES profile will give us ES version 1.1 unless a higher version is requested. + // Requesting version 2.0 will give us the highest compatible version available (2.0, + // 3.0, 3.1, etc). + if (requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE) + { + contextCreationAttributes.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB); + contextCreationAttributes.push_back(2); + + contextCreationAttributes.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); + contextCreationAttributes.push_back(0); + } + } // Set the flag attributes if (flags != 0) @@ -290,6 +327,38 @@ egl::Error DisplayWGL::initialize(egl::Display *display) mFunctionsGL = new FunctionsGLWindows(mOpenGLModule, mFunctionsWGL->getProcAddress); mFunctionsGL->initialize(); + // Intel OpenGL ES drivers are not currently supported due to bugs in the driver and ANGLE + VendorID vendor = GetVendorID(mFunctionsGL); + if (requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE && vendor == VENDOR_ID_INTEL) + { + return egl::Error(EGL_NOT_INITIALIZED, "Intel OpenGL ES drivers are not supported."); + } + + // Create DXGI swap chains for windows that come from other processes. Windows is unable to + // SetPixelFormat on windows from other processes when a sandbox is enabled. + HDC nativeDisplay = display->getNativeDisplayId(); + HWND nativeWindow = WindowFromDC(nativeDisplay); + if (nativeWindow != nullptr) + { + DWORD currentProcessId = GetCurrentProcessId(); + DWORD windowProcessId; + GetWindowThreadProcessId(nativeWindow, &windowProcessId); + mUseDXGISwapChains = (currentProcessId != windowProcessId); + } + else + { + mUseDXGISwapChains = false; + } + + if (mUseDXGISwapChains) + { + egl::Error error = initializeD3DDevice(); + if (error.isError()) + { + return error; + } + } + return DisplayGL::initialize(display); } @@ -297,6 +366,8 @@ void DisplayWGL::terminate() { DisplayGL::terminate(); + releaseD3DDevice(mD3D11DeviceHandle); + mFunctionsWGL->makeCurrent(mDeviceContext, NULL); mFunctionsWGL->deleteContext(mWGLContext); mWGLContext = NULL; @@ -315,14 +386,40 @@ void DisplayWGL::terminate() FreeLibrary(mOpenGLModule); mOpenGLModule = nullptr; + + SafeRelease(mD3D11Device); + + if (mDxgiModule) + { + FreeLibrary(mDxgiModule); + mDxgiModule = nullptr; + } + + if (mD3d11Module) + { + FreeLibrary(mD3d11Module); + mD3d11Module = nullptr; + } + + ASSERT(mRegisteredD3DDevices.empty()); } SurfaceImpl *DisplayWGL::createWindowSurface(const egl::Config *configuration, EGLNativeWindowType window, const egl::AttributeMap &attribs) { - return new WindowSurfaceWGL(this->getRenderer(), window, mPixelFormat, mWGLContext, - mFunctionsWGL); + EGLint orientation = attribs.get(EGL_SURFACE_ORIENTATION_ANGLE, 0); + if (mUseDXGISwapChains) + { + return new DXGISwapChainWindowSurfaceWGL(this->getRenderer(), window, mD3D11Device, + mD3D11DeviceHandle, mWGLContext, mDeviceContext, + mFunctionsGL, mFunctionsWGL, orientation); + } + else + { + return new WindowSurfaceWGL(this->getRenderer(), window, mPixelFormat, mWGLContext, + mFunctionsWGL, orientation); + } } SurfaceImpl *DisplayWGL::createPbufferSurface(const egl::Config *configuration, @@ -385,6 +482,9 @@ egl::ConfigSet DisplayWGL::generateConfigs() const return wgl::QueryWGLFormatAttrib(mDeviceContext, mPixelFormat, attrib, mFunctionsWGL); }; + const EGLint optimalSurfaceOrientation = + mUseDXGISwapChains ? EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE : 0; + egl::Config config; config.renderTargetFormat = GL_RGBA8; // TODO: use the bit counts to determine the format config.depthStencilFormat = GL_DEPTH24_STENCIL8; // TODO: use the bit counts to determine the format @@ -420,6 +520,8 @@ egl::ConfigSet DisplayWGL::generateConfigs() const ((getAttrib(WGL_DRAW_TO_PBUFFER_ARB) == TRUE) ? EGL_PBUFFER_BIT : 0) | ((getAttrib(WGL_SWAP_METHOD_ARB) == WGL_SWAP_COPY_ARB) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0); + config.optimalOrientation = optimalSurfaceOrientation; + config.transparentType = EGL_NONE; config.transparentRedValue = 0; config.transparentGreenValue = 0; @@ -464,9 +566,60 @@ const FunctionsGL *DisplayWGL::getFunctionsGL() const return mFunctionsGL; } +egl::Error DisplayWGL::initializeD3DDevice() +{ + if (mD3D11Device != nullptr) + { + return egl::Error(EGL_SUCCESS); + } + + mDxgiModule = LoadLibrary(TEXT("dxgi.dll")); + if (!mDxgiModule) + { + return egl::Error(EGL_NOT_INITIALIZED, "Failed to load DXGI library."); + } + + mD3d11Module = LoadLibrary(TEXT("d3d11.dll")); + if (!mD3d11Module) + { + return egl::Error(EGL_NOT_INITIALIZED, "Failed to load d3d11 library."); + } + + PFN_D3D11_CREATE_DEVICE d3d11CreateDevice = nullptr; + d3d11CreateDevice = reinterpret_cast( + GetProcAddress(mD3d11Module, "D3D11CreateDevice")); + if (d3d11CreateDevice == nullptr) + { + return egl::Error(EGL_NOT_INITIALIZED, "Could not retrieve D3D11CreateDevice address."); + } + + HRESULT result = d3d11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, + D3D11_SDK_VERSION, &mD3D11Device, nullptr, nullptr); + if (FAILED(result)) + { + return egl::Error(EGL_NOT_INITIALIZED, "Could not create D3D11 device, error: 0x%X", + result); + } + + egl::Error error = registerD3DDevice(mD3D11Device, &mD3D11DeviceHandle); + if (error.isError()) + { + return error; + } + + return egl::Error(EGL_SUCCESS); +} + void DisplayWGL::generateExtensions(egl::DisplayExtensions *outExtensions) const { outExtensions->createContext = true; + outExtensions->createContextNoError = true; + + // Only enable the surface orientation and post sub buffer for DXGI swap chain surfaces, they + // prefer to swap with + // inverted Y. + outExtensions->postSubBuffer = mUseDXGISwapChains; + outExtensions->surfaceOrientation = mUseDXGISwapChains; } void DisplayWGL::generateCaps(egl::Caps *outCaps) const @@ -474,4 +627,65 @@ void DisplayWGL::generateCaps(egl::Caps *outCaps) const outCaps->textureNPOT = true; } +egl::Error DisplayWGL::waitClient() const +{ + // Unimplemented as this is not needed for WGL + return egl::Error(EGL_SUCCESS); +} + +egl::Error DisplayWGL::waitNative(EGLint engine, + egl::Surface *drawSurface, + egl::Surface *readSurface) const +{ + // Unimplemented as this is not needed for WGL + return egl::Error(EGL_SUCCESS); +} + +egl::Error DisplayWGL::registerD3DDevice(IUnknown *device, HANDLE *outHandle) +{ + ASSERT(device != nullptr); + ASSERT(outHandle != nullptr); + + auto iter = mRegisteredD3DDevices.find(device); + if (iter != mRegisteredD3DDevices.end()) + { + iter->second.refCount++; + *outHandle = iter->second.handle; + return egl::Error(EGL_SUCCESS); + } + + HANDLE handle = mFunctionsWGL->dxOpenDeviceNV(device); + if (!handle) + { + return egl::Error(EGL_BAD_PARAMETER, "Failed to open D3D device."); + } + + device->AddRef(); + + D3DObjectHandle newDeviceInfo; + newDeviceInfo.handle = handle; + newDeviceInfo.refCount = 1; + mRegisteredD3DDevices[device] = newDeviceInfo; + + *outHandle = handle; + return egl::Error(EGL_SUCCESS); +} + +void DisplayWGL::releaseD3DDevice(HANDLE deviceHandle) +{ + for (auto iter = mRegisteredD3DDevices.begin(); iter != mRegisteredD3DDevices.end(); iter++) + { + if (iter->second.handle == deviceHandle) + { + iter->second.refCount--; + if (iter->second.refCount == 0) + { + mFunctionsWGL->dxCloseDeviceNV(iter->second.handle); + iter->first->Release(); + mRegisteredD3DDevices.erase(iter); + break; + } + } + } +} } diff --git a/gfx/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.h b/gfx/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.h index 94c3947b6f..bbce4c0596 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.h @@ -52,9 +52,19 @@ class DisplayWGL : public DisplayGL std::string getVendorString() const override; + egl::Error waitClient() const override; + egl::Error waitNative(EGLint engine, + egl::Surface *drawSurface, + egl::Surface *readSurface) const override; + + egl::Error registerD3DDevice(IUnknown *device, HANDLE *outHandle); + void releaseD3DDevice(HANDLE handle); + private: const FunctionsGL *getFunctionsGL() const override; + egl::Error initializeD3DDevice(); + void generateExtensions(egl::DisplayExtensions *outExtensions) const override; void generateCaps(egl::Caps *outCaps) const override; @@ -69,6 +79,19 @@ class DisplayWGL : public DisplayGL int mPixelFormat; HGLRC mWGLContext; + bool mUseDXGISwapChains; + HMODULE mDxgiModule; + HMODULE mD3d11Module; + HANDLE mD3D11DeviceHandle; + ID3D11Device *mD3D11Device; + + struct D3DObjectHandle + { + HANDLE handle; + size_t refCount; + }; + std::map mRegisteredD3DDevices; + egl::Display *mDisplay; }; diff --git a/gfx/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.cpp index 70e9bd8084..2cfe6e9eb9 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.cpp @@ -85,7 +85,15 @@ FunctionsWGL::FunctionsWGL() queryPbufferARB(nullptr), bindTexImageARB(nullptr), releaseTexImageARB(nullptr), - setPbufferAttribARB(nullptr) + setPbufferAttribARB(nullptr), + dxSetResourceShareHandleNV(nullptr), + dxOpenDeviceNV(nullptr), + dxCloseDeviceNV(nullptr), + dxRegisterObjectNV(nullptr), + dxUnregisterObjectNV(nullptr), + dxObjectAccessNV(nullptr), + dxLockObjectsNV(nullptr), + dxUnlockObjectsNV(nullptr) { } @@ -154,6 +162,16 @@ void FunctionsWGL::initialize(HMODULE glModule, HDC context) GetWGLExtensionProcAddress(glModule, getProcAddress, extensions, "WGL_ARB_render_texture", "wglBindTexImageARB", &bindTexImageARB); GetWGLExtensionProcAddress(glModule, getProcAddress, extensions, "WGL_ARB_render_texture", "wglReleaseTexImageARB", &releaseTexImageARB); GetWGLExtensionProcAddress(glModule, getProcAddress, extensions, "WGL_ARB_render_texture", "wglSetPbufferAttribARB", &setPbufferAttribARB); + + // WGL_NV_DX_interop + GetWGLExtensionProcAddress(glModule, getProcAddress, extensions, "WGL_NV_DX_interop", "wglDXSetResourceShareHandleNV", &dxSetResourceShareHandleNV); + GetWGLExtensionProcAddress(glModule, getProcAddress, extensions, "WGL_NV_DX_interop", "wglDXOpenDeviceNV", &dxOpenDeviceNV); + GetWGLExtensionProcAddress(glModule, getProcAddress, extensions, "WGL_NV_DX_interop", "wglDXCloseDeviceNV", &dxCloseDeviceNV); + GetWGLExtensionProcAddress(glModule, getProcAddress, extensions, "WGL_NV_DX_interop", "wglDXRegisterObjectNV", &dxRegisterObjectNV); + GetWGLExtensionProcAddress(glModule, getProcAddress, extensions, "WGL_NV_DX_interop", "wglDXUnregisterObjectNV", &dxUnregisterObjectNV); + GetWGLExtensionProcAddress(glModule, getProcAddress, extensions, "WGL_NV_DX_interop", "wglDXObjectAccessNV", &dxObjectAccessNV); + GetWGLExtensionProcAddress(glModule, getProcAddress, extensions, "WGL_NV_DX_interop", "wglDXLockObjectsNV", &dxLockObjectsNV); + GetWGLExtensionProcAddress(glModule, getProcAddress, extensions, "WGL_NV_DX_interop", "wglDXUnlockObjectsNV", &dxUnlockObjectsNV); } bool FunctionsWGL::hasExtension(const std::string &ext) const diff --git a/gfx/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.h b/gfx/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.h index 7bba6d50ba..30cf9ebc0b 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.h @@ -77,6 +77,16 @@ class FunctionsWGL : angle::NonCopyable PFNWGLBINDTEXIMAGEARBPROC bindTexImageARB; PFNWGLRELEASETEXIMAGEARBPROC releaseTexImageARB; PFNWGLSETPBUFFERATTRIBARBPROC setPbufferAttribARB; + + // WGL_NV_DX_interop + PFNWGLDXSETRESOURCESHAREHANDLENVPROC dxSetResourceShareHandleNV; + PFNWGLDXOPENDEVICENVPROC dxOpenDeviceNV; + PFNWGLDXCLOSEDEVICENVPROC dxCloseDeviceNV; + PFNWGLDXREGISTEROBJECTNVPROC dxRegisterObjectNV; + PFNWGLDXUNREGISTEROBJECTNVPROC dxUnregisterObjectNV; + PFNWGLDXOBJECTACCESSNVPROC dxObjectAccessNV; + PFNWGLDXLOCKOBJECTSNVPROC dxLockObjectsNV; + PFNWGLDXUNLOCKOBJECTSNVPROC dxUnlockObjectsNV; }; } diff --git a/gfx/angle/src/libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.cpp index 0615c879a0..440b9934c4 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.cpp @@ -146,7 +146,7 @@ static int GetWGLBufferBindTarget(EGLint buffer) } } -egl::Error PbufferSurfaceWGL::bindTexImage(EGLint buffer) +egl::Error PbufferSurfaceWGL::bindTexImage(gl::Texture *texture, EGLint buffer) { if (!mFunctionsWGL->bindTexImageARB(mPbuffer, GetWGLBufferBindTarget(buffer))) { diff --git a/gfx/angle/src/libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.h b/gfx/angle/src/libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.h index 3deab4b5cd..64e4de16c6 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.h @@ -39,7 +39,7 @@ class PbufferSurfaceWGL : public SurfaceGL egl::Error swap() override; egl::Error postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) override; egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override; - egl::Error bindTexImage(EGLint buffer) override; + egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override; egl::Error releaseTexImage(EGLint buffer) override; void setSwapInterval(EGLint interval) override; diff --git a/gfx/angle/src/libANGLE/renderer/gl/wgl/WindowSurfaceWGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/wgl/WindowSurfaceWGL.cpp index 9c898a70d7..1858dabd8a 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/wgl/WindowSurfaceWGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/wgl/WindowSurfaceWGL.cpp @@ -20,7 +20,8 @@ WindowSurfaceWGL::WindowSurfaceWGL(RendererGL *renderer, EGLNativeWindowType window, int pixelFormat, HGLRC wglContext, - const FunctionsWGL *functions) + const FunctionsWGL *functions, + EGLint orientation) : SurfaceGL(renderer), mPixelFormat(pixelFormat), mWGLContext(wglContext), @@ -29,6 +30,8 @@ WindowSurfaceWGL::WindowSurfaceWGL(RendererGL *renderer, mFunctionsWGL(functions), mSwapBehavior(0) { + // EGL_ANGLE_surface_orientation is not supported for regular WGL window surfaces + ASSERT(orientation == 0); } WindowSurfaceWGL::~WindowSurfaceWGL() @@ -119,7 +122,7 @@ egl::Error WindowSurfaceWGL::querySurfacePointerANGLE(EGLint attribute, void **v return egl::Error(EGL_SUCCESS); } -egl::Error WindowSurfaceWGL::bindTexImage(EGLint buffer) +egl::Error WindowSurfaceWGL::bindTexImage(gl::Texture *texture, EGLint buffer) { UNIMPLEMENTED(); return egl::Error(EGL_SUCCESS); diff --git a/gfx/angle/src/libANGLE/renderer/gl/wgl/WindowSurfaceWGL.h b/gfx/angle/src/libANGLE/renderer/gl/wgl/WindowSurfaceWGL.h index 568db3d542..e7051b77e9 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/wgl/WindowSurfaceWGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/wgl/WindowSurfaceWGL.h @@ -25,7 +25,8 @@ class WindowSurfaceWGL : public SurfaceGL EGLNativeWindowType window, int pixelFormat, HGLRC wglContext, - const FunctionsWGL *functions); + const FunctionsWGL *functions, + EGLint orientation); ~WindowSurfaceWGL() override; egl::Error initialize() override; @@ -34,7 +35,7 @@ class WindowSurfaceWGL : public SurfaceGL egl::Error swap() override; egl::Error postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) override; egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override; - egl::Error bindTexImage(EGLint buffer) override; + egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override; egl::Error releaseTexImage(EGLint buffer) override; void setSwapInterval(EGLint interval) override; diff --git a/gfx/angle/src/libANGLE/validationEGL.cpp b/gfx/angle/src/libANGLE/validationEGL.cpp index a22ecc9749..bff3c944cf 100644 --- a/gfx/angle/src/libANGLE/validationEGL.cpp +++ b/gfx/angle/src/libANGLE/validationEGL.cpp @@ -11,6 +11,7 @@ #include "common/utilities.h" #include "libANGLE/Config.h" #include "libANGLE/Context.h" +#include "libANGLE/Device.h" #include "libANGLE/Display.h" #include "libANGLE/Image.h" #include "libANGLE/Surface.h" @@ -209,6 +210,9 @@ Error ValidateCreateContext(Display *display, Config *configuration, gl::Context contextFlags = value; break; + case EGL_CONTEXT_OPENGL_DEBUG: + break; + case EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR: // Only valid for OpenGL (non-ES) contexts return Error(EGL_BAD_ATTRIBUTE); @@ -244,6 +248,17 @@ Error ValidateCreateContext(Display *display, Config *configuration, gl::Context } break; + case EGL_CONTEXT_OPENGL_NO_ERROR_KHR: + if (!display->getExtensions().createContextNoError) + { + return Error(EGL_BAD_ATTRIBUTE, "Invalid Context attribute."); + } + if (value != EGL_TRUE && value != EGL_FALSE) + { + return Error(EGL_BAD_ATTRIBUTE, "Attribute must be EGL_TRUE or EGL_FALSE."); + } + break; + default: return Error(EGL_BAD_ATTRIBUTE); } @@ -342,6 +357,13 @@ Error ValidateCreateWindowSurface(Display *display, Config *config, EGLNativeWin } break; + case EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE: + if (!displayExtensions.flexibleSurfaceCompatibility) + { + return Error(EGL_BAD_ATTRIBUTE); + } + break; + case EGL_WIDTH: case EGL_HEIGHT: if (!displayExtensions.windowFixedSize) @@ -361,12 +383,26 @@ Error ValidateCreateWindowSurface(Display *display, Config *config, EGLNativeWin } break; + case EGL_SURFACE_ORIENTATION_ANGLE: + if (!displayExtensions.surfaceOrientation) + { + return Error(EGL_BAD_ATTRIBUTE, "EGL_ANGLE_surface_orientation is not enabled."); + } + break; + case EGL_VG_COLORSPACE: return Error(EGL_BAD_MATCH); case EGL_VG_ALPHA_FORMAT: return Error(EGL_BAD_MATCH); + case EGL_DIRECT_COMPOSITION_ANGLE: + if (!displayExtensions.directComposition) + { + return Error(EGL_BAD_ATTRIBUTE); + } + break; + default: return Error(EGL_BAD_ATTRIBUTE); } @@ -387,7 +423,9 @@ Error ValidateCreatePbufferSurface(Display *display, Config *config, const Attri { return error; } - + + const DisplayExtensions &displayExtensions = display->getExtensions(); + for (AttributeMap::const_iterator attributeIter = attributes.begin(); attributeIter != attributes.end(); attributeIter++) { EGLint attribute = attributeIter->first; @@ -438,6 +476,16 @@ Error ValidateCreatePbufferSurface(Display *display, Config *config, const Attri case EGL_VG_ALPHA_FORMAT: break; + case EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE: + if (!displayExtensions.flexibleSurfaceCompatibility) + { + return Error( + EGL_BAD_ATTRIBUTE, + "EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE cannot be used without " + "EGL_ANGLE_flexible_surface_compatibility support."); + } + break; + default: return Error(EGL_BAD_ATTRIBUTE); } @@ -548,6 +596,16 @@ Error ValidateCreatePbufferFromClientBuffer(Display *display, EGLenum buftype, E case EGL_MIPMAP_TEXTURE: break; + case EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE: + if (!displayExtensions.flexibleSurfaceCompatibility) + { + return Error( + EGL_BAD_ATTRIBUTE, + "EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE cannot be used without " + "EGL_ANGLE_flexible_surface_compatibility support."); + } + break; + default: return Error(EGL_BAD_ATTRIBUTE); } @@ -592,28 +650,38 @@ Error ValidateCreatePbufferFromClientBuffer(Display *display, EGLenum buftype, E return Error(EGL_SUCCESS); } -Error ValidateCompatibleConfigs(const Config *config1, const Config *config2, EGLint surfaceType) +Error ValidateCompatibleConfigs(const Display *display, + const Config *config1, + const Surface *surface, + const Config *config2, + EGLint surfaceType) { - // Config compatibility is defined in section 2.2 of the EGL 1.5 spec - bool colorBufferCompat = config1->colorBufferType == config2->colorBufferType; - if (!colorBufferCompat) + if (!surface->flexibleSurfaceCompatibilityRequested()) { - return Error(EGL_BAD_MATCH, "Color buffer types are not compatible."); - } + // Config compatibility is defined in section 2.2 of the EGL 1.5 spec - bool colorCompat = config1->redSize == config2->redSize && config1->greenSize == config2->greenSize && - config1->blueSize == config2->blueSize && config1->alphaSize == config2->alphaSize && - config1->luminanceSize == config2->luminanceSize; - if (!colorCompat) - { - return Error(EGL_BAD_MATCH, "Color buffer sizes are not compatible."); - } + bool colorBufferCompat = config1->colorBufferType == config2->colorBufferType; + if (!colorBufferCompat) + { + return Error(EGL_BAD_MATCH, "Color buffer types are not compatible."); + } - bool dsCompat = config1->depthSize == config2->depthSize && config1->stencilSize == config2->stencilSize; - if (!dsCompat) - { - return Error(EGL_BAD_MATCH, "Depth-stencil buffer types are not compatible."); + bool colorCompat = + config1->redSize == config2->redSize && config1->greenSize == config2->greenSize && + config1->blueSize == config2->blueSize && config1->alphaSize == config2->alphaSize && + config1->luminanceSize == config2->luminanceSize; + if (!colorCompat) + { + return Error(EGL_BAD_MATCH, "Color buffer sizes are not compatible."); + } + + bool dsCompat = config1->depthSize == config2->depthSize && + config1->stencilSize == config2->stencilSize; + if (!dsCompat) + { + return Error(EGL_BAD_MATCH, "Depth-stencil buffer types are not compatible."); + } } bool surfaceTypeCompat = (config1->surfaceType & config2->surfaceType & surfaceType) != 0; @@ -934,4 +1002,56 @@ Error ValidateDestroyImageKHR(const Display *display, const Image *image) return Error(EGL_SUCCESS); } + +Error ValidateCreateDeviceANGLE(EGLint device_type, + void *native_device, + const EGLAttrib *attrib_list) +{ + const ClientExtensions &clientExtensions = Display::getClientExtensions(); + if (!clientExtensions.deviceCreation) + { + return Error(EGL_BAD_ACCESS, "Device creation extension not active"); + } + + if (attrib_list != nullptr && attrib_list[0] != EGL_NONE) + { + return Error(EGL_BAD_ATTRIBUTE, "Invalid attrib_list parameter"); + } + + switch (device_type) + { + case EGL_D3D11_DEVICE_ANGLE: + if (!clientExtensions.deviceCreationD3D11) + { + return Error(EGL_BAD_ATTRIBUTE, "D3D11 device creation extension not active"); + } + break; + default: + return Error(EGL_BAD_ATTRIBUTE, "Invalid device_type parameter"); + } + + return Error(EGL_SUCCESS); +} + +Error ValidateReleaseDeviceANGLE(Device *device) +{ + const ClientExtensions &clientExtensions = Display::getClientExtensions(); + if (!clientExtensions.deviceCreation) + { + return Error(EGL_BAD_ACCESS, "Device creation extension not active"); + } + + if (device == EGL_NO_DEVICE_EXT || !Device::IsValidDevice(device)) + { + return Error(EGL_BAD_DEVICE_EXT, "Invalid device parameter"); + } + + Display *owningDisplay = device->getOwningDisplay(); + if (owningDisplay != nullptr) + { + return Error(EGL_BAD_DEVICE_EXT, "Device must have been created using eglCreateDevice"); + } + + return Error(EGL_SUCCESS); +} } diff --git a/gfx/angle/src/libANGLE/validationEGL.h b/gfx/angle/src/libANGLE/validationEGL.h index 6cd7d6dc12..eaafddc20d 100644 --- a/gfx/angle/src/libANGLE/validationEGL.h +++ b/gfx/angle/src/libANGLE/validationEGL.h @@ -23,6 +23,7 @@ namespace egl class AttributeMap; struct Config; +class Device; class Display; class Image; class Surface; @@ -52,9 +53,17 @@ Error ValidateCreateImageKHR(const Display *display, const AttributeMap &attributes); Error ValidateDestroyImageKHR(const Display *display, const Image *image); -// Other validation -Error ValidateCompatibleConfigs(const Config *config1, const Config *config2, EGLint surfaceType); +Error ValidateCreateDeviceANGLE(EGLint device_type, + void *native_device, + const EGLAttrib *attrib_list); +Error ValidateReleaseDeviceANGLE(Device *device); +// Other validation +Error ValidateCompatibleConfigs(const Display *display, + const Config *config1, + const Surface *surface, + const Config *config2, + EGLint surfaceType); } #endif // LIBANGLE_VALIDATIONEGL_H_ diff --git a/gfx/angle/src/libANGLE/validationES.cpp b/gfx/angle/src/libANGLE/validationES.cpp index 547f548ca9..12c76120bd 100644 --- a/gfx/angle/src/libANGLE/validationES.cpp +++ b/gfx/angle/src/libANGLE/validationES.cpp @@ -109,15 +109,21 @@ bool ValidCap(const Context *context, GLenum cap) case GL_BLEND: case GL_DITHER: return true; + case GL_PRIMITIVE_RESTART_FIXED_INDEX: case GL_RASTERIZER_DISCARD: return (context->getClientVersion() >= 3); + + case GL_DEBUG_OUTPUT_SYNCHRONOUS: + case GL_DEBUG_OUTPUT: + return context->getExtensions().debug; + default: return false; } } -bool ValidTextureTarget(const Context *context, GLenum target) +bool ValidTextureTarget(const ValidationContext *context, GLenum target) { switch (target) { @@ -134,11 +140,37 @@ bool ValidTextureTarget(const Context *context, GLenum target) } } +bool ValidTexture2DTarget(const ValidationContext *context, GLenum target) +{ + switch (target) + { + case GL_TEXTURE_2D: + case GL_TEXTURE_CUBE_MAP: + return true; + + default: + return false; + } +} + +bool ValidTexture3DTarget(const ValidationContext *context, GLenum target) +{ + switch (target) + { + case GL_TEXTURE_3D: + case GL_TEXTURE_2D_ARRAY: + return (context->getClientVersion() >= 3); + + default: + return false; + } +} + // This function differs from ValidTextureTarget in that the target must be // usable as the destination of a 2D operation-- so a cube face is valid, but // GL_TEXTURE_CUBE_MAP is not. // Note: duplicate of IsInternalTextureTarget -bool ValidTexture2DDestinationTarget(const Context *context, GLenum target) +bool ValidTexture2DDestinationTarget(const ValidationContext *context, GLenum target) { switch (target) { @@ -150,9 +182,18 @@ bool ValidTexture2DDestinationTarget(const Context *context, GLenum target) case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return true; - case GL_TEXTURE_2D_ARRAY: + default: + return false; + } +} + +bool ValidTexture3DDestinationTarget(const ValidationContext *context, GLenum target) +{ + switch (target) + { case GL_TEXTURE_3D: - return (context->getClientVersion() >= 3); + case GL_TEXTURE_2D_ARRAY: + return true; default: return false; } @@ -224,21 +265,30 @@ bool ValidBufferParameter(const Context *context, GLenum pname) } } -bool ValidMipLevel(const Context *context, GLenum target, GLint level) +bool ValidMipLevel(const ValidationContext *context, GLenum target, GLint level) { + const auto &caps = context->getCaps(); size_t maxDimension = 0; switch (target) { - case GL_TEXTURE_2D: maxDimension = context->getCaps().max2DTextureSize; break; + case GL_TEXTURE_2D: + maxDimension = caps.max2DTextureSize; + break; case GL_TEXTURE_CUBE_MAP: case GL_TEXTURE_CUBE_MAP_POSITIVE_X: case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: - case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: maxDimension = context->getCaps().maxCubeMapTextureSize; break; - case GL_TEXTURE_3D: maxDimension = context->getCaps().max3DTextureSize; break; - case GL_TEXTURE_2D_ARRAY: maxDimension = context->getCaps().max2DTextureSize; break; + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + maxDimension = caps.maxCubeMapTextureSize; + break; + case GL_TEXTURE_3D: + maxDimension = caps.max3DTextureSize; + break; + case GL_TEXTURE_2D_ARRAY: + maxDimension = caps.max2DTextureSize; + break; default: UNREACHABLE(); } @@ -284,6 +334,7 @@ bool CompressedTextureFormatRequiresExactSize(GLenum internalFormat) case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE: case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE: + case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: return true; default: @@ -291,7 +342,10 @@ bool CompressedTextureFormatRequiresExactSize(GLenum internalFormat) } } -bool ValidCompressedImageSize(const Context *context, GLenum internalFormat, GLsizei width, GLsizei height) +bool ValidCompressedImageSize(const ValidationContext *context, + GLenum internalFormat, + GLsizei width, + GLsizei height) { const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat); if (!formatInfo.compressed) @@ -330,6 +384,8 @@ bool ValidQueryType(const Context *context, GLenum queryType) return true; case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: return (context->getClientVersion() >= 3); + case GL_TIME_ELAPSED_EXT: + return context->getExtensions().disjointTimerQuery; default: return false; } @@ -539,44 +595,23 @@ bool ValidateFramebufferRenderbufferParameters(gl::Context *context, GLenum targ return true; } -static bool IsPartialBlit(gl::Context *context, const gl::FramebufferAttachment *readBuffer, const gl::FramebufferAttachment *writeBuffer, - GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, - GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1) -{ - if (srcX0 != 0 || srcY0 != 0 || dstX0 != 0 || dstY0 != 0 || - dstX1 != writeBuffer->getWidth() || dstY1 != writeBuffer->getHeight() || - srcX1 != readBuffer->getWidth() || srcY1 != readBuffer->getHeight()) - { - return true; - } - else if (context->getState().isScissorTestEnabled()) - { - const Rectangle &scissor = context->getState().getScissor(); - - return scissor.x > 0 || scissor.y > 0 || - scissor.width < writeBuffer->getWidth() || - scissor.height < writeBuffer->getHeight(); - } - else - { - return false; - } -} - -bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, - GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, - GLenum filter, bool fromAngleExtension) +bool ValidateBlitFramebufferParameters(gl::Context *context, + GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter) { switch (filter) { case GL_NEAREST: break; case GL_LINEAR: - if (fromAngleExtension) - { - context->recordError(Error(GL_INVALID_ENUM)); - return false; - } break; default: context->recordError(Error(GL_INVALID_ENUM)); @@ -596,13 +631,6 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint return false; } - if (fromAngleExtension && (srcX1 - srcX0 != dstX1 - dstX0 || srcY1 - srcY0 != dstY1 - dstY0)) - { - ERR("Scaling and flipping in BlitFramebufferANGLE not supported by this implementation."); - context->recordError(Error(GL_INVALID_OPERATION)); - return false; - } - // ES3.0 spec, section 4.3.2 states that linear filtering is only available for the // color buffer, leaving only nearest being unfiltered from above if ((mask & ~GL_COLOR_BUFFER_BIT) != 0 && filter != GL_NEAREST) @@ -613,11 +641,6 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint if (context->getState().getReadFramebuffer()->id() == context->getState().getDrawFramebuffer()->id()) { - if (fromAngleExtension) - { - ERR("Blits with the same source and destination framebuffer are not supported by this " - "implementation."); - } context->recordError(Error(GL_INVALID_OPERATION)); return false; } @@ -655,37 +678,66 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint { const gl::FramebufferAttachment *readColorBuffer = readFramebuffer->getReadColorbuffer(); const gl::FramebufferAttachment *drawColorBuffer = drawFramebuffer->getFirstColorbuffer(); + const Extensions &extensions = context->getExtensions(); if (readColorBuffer && drawColorBuffer) { GLenum readInternalFormat = readColorBuffer->getInternalFormat(); const InternalFormat &readFormatInfo = GetInternalFormatInfo(readInternalFormat); - for (size_t i = 0; i < drawFramebuffer->getNumColorBuffers(); i++) + for (size_t drawbufferIdx = 0; + drawbufferIdx < drawFramebuffer->getDrawbufferStateCount(); ++drawbufferIdx) { - if (drawFramebuffer->isEnabledColorAttachment(i)) + const FramebufferAttachment *attachment = + drawFramebuffer->getDrawBuffer(drawbufferIdx); + if (attachment) { - GLenum drawInternalFormat = drawFramebuffer->getColorbuffer(i)->getInternalFormat(); + GLenum drawInternalFormat = attachment->getInternalFormat(); const InternalFormat &drawFormatInfo = GetInternalFormatInfo(drawInternalFormat); // The GL ES 3.0.2 spec (pg 193) states that: // 1) If the read buffer is fixed point format, the draw buffer must be as well // 2) If the read buffer is an unsigned integer format, the draw buffer must be as well // 3) If the read buffer is a signed integer format, the draw buffer must be as well - if ( (readFormatInfo.componentType == GL_UNSIGNED_NORMALIZED || readFormatInfo.componentType == GL_SIGNED_NORMALIZED) && - !(drawFormatInfo.componentType == GL_UNSIGNED_NORMALIZED || drawFormatInfo.componentType == GL_SIGNED_NORMALIZED)) + // Changes with EXT_color_buffer_float: + // Case 1) is changed to fixed point OR floating point + GLenum readComponentType = readFormatInfo.componentType; + GLenum drawComponentType = drawFormatInfo.componentType; + bool readFixedPoint = (readComponentType == GL_UNSIGNED_NORMALIZED || + readComponentType == GL_SIGNED_NORMALIZED); + bool drawFixedPoint = (drawComponentType == GL_UNSIGNED_NORMALIZED || + drawComponentType == GL_SIGNED_NORMALIZED); + + if (extensions.colorBufferFloat) + { + bool readFixedOrFloat = (readFixedPoint || readComponentType == GL_FLOAT); + bool drawFixedOrFloat = (drawFixedPoint || drawComponentType == GL_FLOAT); + + if (readFixedOrFloat != drawFixedOrFloat) + { + context->recordError(Error(GL_INVALID_OPERATION, + "If the read buffer contains fixed-point or " + "floating-point values, the draw buffer " + "must as well.")); + return false; + } + } + else if (readFixedPoint != drawFixedPoint) + { + context->recordError(Error(GL_INVALID_OPERATION, + "If the read buffer contains fixed-point " + "values, the draw buffer must as well.")); + return false; + } + + if (readComponentType == GL_UNSIGNED_INT && + drawComponentType != GL_UNSIGNED_INT) { context->recordError(Error(GL_INVALID_OPERATION)); return false; } - if (readFormatInfo.componentType == GL_UNSIGNED_INT && drawFormatInfo.componentType != GL_UNSIGNED_INT) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return false; - } - - if (readFormatInfo.componentType == GL_INT && drawFormatInfo.componentType != GL_INT) + if (readComponentType == GL_INT && drawComponentType != GL_INT) { context->recordError(Error(GL_INVALID_OPERATION)); return false; @@ -704,54 +756,6 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint context->recordError(Error(GL_INVALID_OPERATION)); return false; } - - if (fromAngleExtension) - { - const FramebufferAttachment *readColorAttachment = readFramebuffer->getReadColorbuffer(); - if (!readColorAttachment || - (!(readColorAttachment->type() == GL_TEXTURE && readColorAttachment->getTextureImageIndex().type == GL_TEXTURE_2D) && - readColorAttachment->type() != GL_RENDERBUFFER && - readColorAttachment->type() != GL_FRAMEBUFFER_DEFAULT)) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return false; - } - - for (size_t colorAttachment = 0; - colorAttachment < drawFramebuffer->getNumColorBuffers(); ++colorAttachment) - { - if (drawFramebuffer->isEnabledColorAttachment(colorAttachment)) - { - const FramebufferAttachment *attachment = drawFramebuffer->getColorbuffer(colorAttachment); - ASSERT(attachment); - - if (!(attachment->type() == GL_TEXTURE && attachment->getTextureImageIndex().type == GL_TEXTURE_2D) && - attachment->type() != GL_RENDERBUFFER && - attachment->type() != GL_FRAMEBUFFER_DEFAULT) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return false; - } - - // Return an error if the destination formats do not match - if (attachment->getInternalFormat() != readColorBuffer->getInternalFormat()) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return false; - } - } - } - - int readSamples = readFramebuffer->getSamples(context->getData()); - - if (readSamples != 0 && IsPartialBlit(context, readColorBuffer, drawColorBuffer, - srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1)) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return false; - } - } } } @@ -777,23 +781,6 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint context->recordError(Error(GL_INVALID_OPERATION)); return false; } - - if (fromAngleExtension) - { - if (IsPartialBlit(context, readBuffer, drawBuffer, - srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1)) - { - ERR("Only whole-buffer depth and stencil blits are supported by this implementation."); - context->recordError(Error(GL_INVALID_OPERATION)); // only whole-buffer copies are permitted - return false; - } - - if (readBuffer->getSamples() != 0 || drawBuffer->getSamples() != 0) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return false; - } - } } } } @@ -1023,10 +1010,22 @@ bool ValidateSamplerObjectParameter(gl::Context *context, GLenum pname) } } -bool ValidateReadPixelsParameters(gl::Context *context, GLint x, GLint y, GLsizei width, GLsizei height, - GLenum format, GLenum type, GLsizei *bufSize, GLvoid *pixels) +bool ValidateReadPixels(Context *context, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLvoid *pixels) { - gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer(); + if (width < 0 || height < 0) + { + context->recordError(Error(GL_INVALID_VALUE, "width and height must be positive")); + return false; + } + + Framebuffer *framebuffer = context->getState().getReadFramebuffer(); ASSERT(framebuffer); if (framebuffer->checkStatus(context->getData()) != GL_FRAMEBUFFER_COMPLETE) @@ -1063,6 +1062,25 @@ bool ValidateReadPixelsParameters(gl::Context *context, GLint x, GLint y, GLsize return false; } + return true; +} + +bool ValidateReadnPixelsEXT(Context *context, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLsizei bufSize, + GLvoid *pixels) +{ + if (bufSize < 0) + { + context->recordError(Error(GL_INVALID_VALUE, "bufSize must be a positive number")); + return false; + } + GLenum sizedInternalFormat = GetSizedInternalFormat(format, type); const InternalFormat &sizedFormatInfo = GetInternalFormatInfo(sizedInternalFormat); @@ -1070,30 +1088,73 @@ bool ValidateReadPixelsParameters(gl::Context *context, GLint x, GLint y, GLsize sizedFormatInfo.computeRowPitch(type, width, context->getState().getPackAlignment(), context->getState().getPackRowLength()); // sized query sanity check - if (bufSize) + int requiredSize = outputPitch * height; + if (requiredSize > bufSize) { - int requiredSize = outputPitch * height; - if (requiredSize > *bufSize) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return false; - } + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + return ValidateReadPixels(context, x, y, width, height, format, type, pixels); +} + +bool ValidateGenQueriesBase(gl::Context *context, GLsizei n, const GLuint *ids) +{ + if (n < 0) + { + context->recordError(Error(GL_INVALID_VALUE, "Query count < 0")); + return false; } return true; } -bool ValidateBeginQuery(gl::Context *context, GLenum target, GLuint id) +bool ValidateGenQueriesEXT(gl::Context *context, GLsizei n, const GLuint *ids) +{ + if (!context->getExtensions().occlusionQueryBoolean && + !context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query extension not enabled")); + return false; + } + + return ValidateGenQueriesBase(context, n, ids); +} + +bool ValidateDeleteQueriesBase(gl::Context *context, GLsizei n, const GLuint *ids) +{ + if (n < 0) + { + context->recordError(Error(GL_INVALID_VALUE, "Query count < 0")); + return false; + } + + return true; +} + +bool ValidateDeleteQueriesEXT(gl::Context *context, GLsizei n, const GLuint *ids) +{ + if (!context->getExtensions().occlusionQueryBoolean && + !context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query extension not enabled")); + return false; + } + + return ValidateDeleteQueriesBase(context, n, ids); +} + +bool ValidateBeginQueryBase(gl::Context *context, GLenum target, GLuint id) { if (!ValidQueryType(context, target)) { - context->recordError(Error(GL_INVALID_ENUM)); + context->recordError(Error(GL_INVALID_ENUM, "Invalid query target")); return false; } if (id == 0) { - context->recordError(Error(GL_INVALID_OPERATION)); + context->recordError(Error(GL_INVALID_OPERATION, "Query id is 0")); return false; } @@ -1112,9 +1173,12 @@ bool ValidateBeginQuery(gl::Context *context, GLenum target, GLuint id) // b) There are no active queries for the requested target (and in the case // of GL_ANY_SAMPLES_PASSED_EXT and GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, // no query may be active for either if glBeginQuery targets either. + + // TODO(ewell): I think this needs to be changed for timer and occlusion queries to work at the + // same time if (context->getState().isQueryActive()) { - context->recordError(Error(GL_INVALID_OPERATION)); + context->recordError(Error(GL_INVALID_OPERATION, "Other query is active")); return false; } @@ -1123,39 +1187,210 @@ bool ValidateBeginQuery(gl::Context *context, GLenum target, GLuint id) // check that name was obtained with glGenQueries if (!queryObject) { - context->recordError(Error(GL_INVALID_OPERATION)); + context->recordError(Error(GL_INVALID_OPERATION, "Invalid query id")); return false; } // check for type mismatch if (queryObject->getType() != target) { - context->recordError(Error(GL_INVALID_OPERATION)); + context->recordError(Error(GL_INVALID_OPERATION, "Query type does not match target")); return false; } return true; } -bool ValidateEndQuery(gl::Context *context, GLenum target) +bool ValidateBeginQueryEXT(gl::Context *context, GLenum target, GLuint id) +{ + if (!context->getExtensions().occlusionQueryBoolean && + !context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query extension not enabled")); + return false; + } + + return ValidateBeginQueryBase(context, target, id); +} + +bool ValidateEndQueryBase(gl::Context *context, GLenum target) { if (!ValidQueryType(context, target)) { - context->recordError(Error(GL_INVALID_ENUM)); + context->recordError(Error(GL_INVALID_ENUM, "Invalid query target")); return false; } const Query *queryObject = context->getState().getActiveQuery(target); - if (queryObject == NULL) + if (queryObject == nullptr) { - context->recordError(Error(GL_INVALID_OPERATION)); + context->recordError(Error(GL_INVALID_OPERATION, "Query target not active")); return false; } return true; } +bool ValidateEndQueryEXT(gl::Context *context, GLenum target) +{ + if (!context->getExtensions().occlusionQueryBoolean && + !context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query extension not enabled")); + return false; + } + + return ValidateEndQueryBase(context, target); +} + +bool ValidateQueryCounterEXT(Context *context, GLuint id, GLenum target) +{ + if (!context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Disjoint timer query not enabled")); + return false; + } + + if (target != GL_TIMESTAMP_EXT) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid query target")); + return false; + } + + Query *queryObject = context->getQuery(id, true, target); + if (queryObject == nullptr) + { + context->recordError(Error(GL_INVALID_OPERATION, "Invalid query id")); + return false; + } + + if (context->getState().isQueryActive(queryObject)) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query is active")); + return false; + } + + return true; +} + +bool ValidateGetQueryivBase(Context *context, GLenum target, GLenum pname) +{ + if (!ValidQueryType(context, target) && target != GL_TIMESTAMP_EXT) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid query type")); + return false; + } + + switch (pname) + { + case GL_CURRENT_QUERY_EXT: + if (target == GL_TIMESTAMP_EXT) + { + context->recordError( + Error(GL_INVALID_ENUM, "Cannot use current query for timestamp")); + return false; + } + break; + case GL_QUERY_COUNTER_BITS_EXT: + if (!context->getExtensions().disjointTimerQuery || + (target != GL_TIMESTAMP_EXT && target != GL_TIME_ELAPSED_EXT)) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid pname")); + return false; + } + break; + default: + context->recordError(Error(GL_INVALID_ENUM, "Invalid pname")); + return false; + } + + return true; +} + +bool ValidateGetQueryivEXT(Context *context, GLenum target, GLenum pname, GLint *params) +{ + if (!context->getExtensions().occlusionQueryBoolean && + !context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query extension not enabled")); + return false; + } + + return ValidateGetQueryivBase(context, target, pname); +} + +bool ValidateGetQueryObjectValueBase(Context *context, GLuint id, GLenum pname) +{ + Query *queryObject = context->getQuery(id, false, GL_NONE); + + if (!queryObject) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query does not exist")); + return false; + } + + if (context->getState().isQueryActive(queryObject)) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query currently active")); + return false; + } + + switch (pname) + { + case GL_QUERY_RESULT_EXT: + case GL_QUERY_RESULT_AVAILABLE_EXT: + break; + + default: + context->recordError(Error(GL_INVALID_ENUM, "Invalid pname enum")); + return false; + } + + return true; +} + +bool ValidateGetQueryObjectivEXT(Context *context, GLuint id, GLenum pname, GLint *params) +{ + if (!context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Timer query extension not enabled")); + return false; + } + return ValidateGetQueryObjectValueBase(context, id, pname); +} + +bool ValidateGetQueryObjectuivEXT(Context *context, GLuint id, GLenum pname, GLuint *params) +{ + if (!context->getExtensions().disjointTimerQuery && + !context->getExtensions().occlusionQueryBoolean) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query extension not enabled")); + return false; + } + return ValidateGetQueryObjectValueBase(context, id, pname); +} + +bool ValidateGetQueryObjecti64vEXT(Context *context, GLuint id, GLenum pname, GLint64 *params) +{ + if (!context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Timer query extension not enabled")); + return false; + } + return ValidateGetQueryObjectValueBase(context, id, pname); +} + +bool ValidateGetQueryObjectui64vEXT(Context *context, GLuint id, GLenum pname, GLuint64 *params) +{ + if (!context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Timer query extension not enabled")); + return false; + } + return ValidateGetQueryObjectValueBase(context, id, pname); +} + static bool ValidateUniformCommonBase(gl::Context *context, GLenum targetUniformType, GLint location, @@ -1326,17 +1561,21 @@ bool ValidateStateQuery(gl::Context *context, GLenum pname, GLenum *nativeType, return true; } -bool ValidateCopyTexImageParametersBase(gl::Context *context, GLenum target, GLint level, GLenum internalformat, bool isSubImage, - GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, - GLint border, GLenum *textureFormatOut) +bool ValidateCopyTexImageParametersBase(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border, + GLenum *textureFormatOut) { - - if (!ValidTexture2DDestinationTarget(context, target)) - { - context->recordError(Error(GL_INVALID_ENUM)); - return false; - } - if (level < 0 || xoffset < 0 || yoffset < 0 || zoffset < 0 || width < 0 || height < 0) { context->recordError(Error(GL_INVALID_VALUE)); @@ -1361,14 +1600,15 @@ bool ValidateCopyTexImageParametersBase(gl::Context *context, GLenum target, GLi return false; } - gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer(); + const gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer(); if (framebuffer->checkStatus(context->getData()) != GL_FRAMEBUFFER_COMPLETE) { context->recordError(Error(GL_INVALID_FRAMEBUFFER_OPERATION)); return false; } - if (context->getState().getReadFramebuffer()->id() != 0 && framebuffer->getSamples(context->getData()) != 0) + const auto &state = context->getState(); + if (state.getReadFramebuffer()->id() != 0 && framebuffer->getSamples(context->getData()) != 0) { context->recordError(Error(GL_INVALID_OPERATION)); return false; @@ -1405,7 +1645,8 @@ bool ValidateCopyTexImageParametersBase(gl::Context *context, GLenum target, GLi return false; } - gl::Texture *texture = context->getTargetTexture(IsCubeMapTextureTarget(target) ? GL_TEXTURE_CUBE_MAP : target); + gl::Texture *texture = + state.getTargetTexture(IsCubeMapTextureTarget(target) ? GL_TEXTURE_CUBE_MAP : target); if (!texture) { context->recordError(Error(GL_INVALID_OPERATION)); @@ -2190,9 +2431,7 @@ bool ValidateEGLImageTargetRenderbufferStorageOES(Context *context, bool ValidateBindVertexArrayBase(Context *context, GLuint array) { - VertexArray *vao = context->getVertexArray(array); - - if (!vao) + if (!context->isVertexArrayGenerated(array)) { // The default VAO should always exist ASSERT(array != 0); @@ -2224,4 +2463,173 @@ bool ValidateGenVertexArraysBase(Context *context, GLsizei n) return true; } + +bool ValidateProgramBinaryBase(Context *context, + GLuint program, + GLenum binaryFormat, + const void *binary, + GLint length) +{ + Program *programObject = GetValidProgram(context, program); + if (programObject == nullptr) + { + return false; + } + + const std::vector &programBinaryFormats = context->getCaps().programBinaryFormats; + if (std::find(programBinaryFormats.begin(), programBinaryFormats.end(), binaryFormat) == + programBinaryFormats.end()) + { + context->recordError(Error(GL_INVALID_ENUM, "Program binary format is not valid.")); + return false; + } + + return true; } + +bool ValidateGetProgramBinaryBase(Context *context, + GLuint program, + GLsizei bufSize, + GLsizei *length, + GLenum *binaryFormat, + void *binary) +{ + Program *programObject = GetValidProgram(context, program); + if (programObject == nullptr) + { + return false; + } + + if (!programObject->isLinked()) + { + context->recordError(Error(GL_INVALID_OPERATION, "Program is not linked.")); + return false; + } + + return true; +} + +bool ValidateCopyTexImage2D(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border) +{ + if (context->getClientVersion() < 3) + { + return ValidateES2CopyTexImageParameters(context, target, level, internalformat, false, 0, + 0, x, y, width, height, border); + } + + ASSERT(context->getClientVersion() == 3); + return ValidateES3CopyTexImage2DParameters(context, target, level, internalformat, false, 0, 0, + 0, x, y, width, height, border); +} + +bool ValidateFramebufferRenderbuffer(Context *context, + GLenum target, + GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer) +{ + if (!ValidFramebufferTarget(target) || + (renderbuffertarget != GL_RENDERBUFFER && renderbuffer != 0)) + { + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + + return ValidateFramebufferRenderbufferParameters(context, target, attachment, + renderbuffertarget, renderbuffer); +} + +bool ValidateDrawBuffersBase(ValidationContext *context, GLsizei n, const GLenum *bufs) +{ + // INVALID_VALUE is generated if n is negative or greater than value of MAX_DRAW_BUFFERS + if (n < 0 || static_cast(n) > context->getCaps().maxDrawBuffers) + { + context->recordError( + Error(GL_INVALID_VALUE, "n must be non-negative and no greater than MAX_DRAW_BUFFERS")); + return false; + } + + ASSERT(context->getState().getDrawFramebuffer()); + GLuint frameBufferId = context->getState().getDrawFramebuffer()->id(); + GLuint maxColorAttachment = GL_COLOR_ATTACHMENT0_EXT + context->getCaps().maxColorAttachments; + + // This should come first before the check for the default frame buffer + // because when we switch to ES3.1+, invalid enums will return INVALID_ENUM + // rather than INVALID_OPERATION + for (int colorAttachment = 0; colorAttachment < n; colorAttachment++) + { + const GLenum attachment = GL_COLOR_ATTACHMENT0_EXT + colorAttachment; + + if (bufs[colorAttachment] != GL_NONE && bufs[colorAttachment] != GL_BACK && + (bufs[colorAttachment] < GL_COLOR_ATTACHMENT0_EXT || + bufs[colorAttachment] >= maxColorAttachment)) + { + // Value in bufs is not NONE, BACK, or GL_COLOR_ATTACHMENTi + // In the 3.0 specs, the error should return GL_INVALID_OPERATION. + // When we move to 3.1 specs, we should change the error to be GL_INVALID_ENUM + context->recordError(Error(GL_INVALID_OPERATION, "Invalid buffer value")); + return false; + } + else if (bufs[colorAttachment] != GL_NONE && bufs[colorAttachment] != attachment && + frameBufferId != 0) + { + // INVALID_OPERATION-GL is bound to buffer and ith argument + // is not COLOR_ATTACHMENTi or NONE + context->recordError( + Error(GL_INVALID_OPERATION, "Ith value does not match COLOR_ATTACHMENTi or NONE")); + return false; + } + } + + // INVALID_OPERATION is generated if GL is bound to the default framebuffer + // and n is not 1 or bufs is bound to value other than BACK and NONE + if (frameBufferId == 0) + { + if (n != 1) + { + context->recordError(Error(GL_INVALID_OPERATION, + "n must be 1 when GL is bound to the default framebuffer")); + return false; + } + + if (bufs[0] != GL_NONE && bufs[0] != GL_BACK) + { + context->recordError(Error( + GL_INVALID_OPERATION, + "Only NONE or BACK are valid values when drawing to the default framebuffer")); + return false; + } + } + + return true; +} + +bool ValidateCopyTexSubImage2D(Context *context, + GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height) +{ + if (context->getClientVersion() < 3) + { + return ValidateES2CopyTexImageParameters(context, target, level, GL_NONE, true, xoffset, + yoffset, x, y, width, height, 0); + } + + return ValidateES3CopyTexImage2DParameters(context, target, level, GL_NONE, true, xoffset, + yoffset, 0, x, y, width, height, 0); +} + +} // namespace gl diff --git a/gfx/angle/src/libANGLE/validationES.h b/gfx/angle/src/libANGLE/validationES.h index 041c725134..5d8486a6ab 100644 --- a/gfx/angle/src/libANGLE/validationES.h +++ b/gfx/angle/src/libANGLE/validationES.h @@ -22,19 +22,21 @@ class Image; namespace gl { - class Context; class Program; class Shader; class ValidationContext; bool ValidCap(const Context *context, GLenum cap); -bool ValidTextureTarget(const Context *context, GLenum target); -bool ValidTexture2DDestinationTarget(const Context *context, GLenum target); +bool ValidTextureTarget(const ValidationContext *context, GLenum target); +bool ValidTexture2DTarget(const ValidationContext *context, GLenum target); +bool ValidTexture3DTarget(const ValidationContext *context, GLenum target); +bool ValidTexture2DDestinationTarget(const ValidationContext *context, GLenum target); +bool ValidTexture3DDestinationTarget(const ValidationContext *context, GLenum target); bool ValidFramebufferTarget(GLenum target); bool ValidBufferTarget(const Context *context, GLenum target); bool ValidBufferParameter(const Context *context, GLenum pname); -bool ValidMipLevel(const Context *context, GLenum target, GLint level); +bool ValidMipLevel(const ValidationContext *context, GLenum target, GLint level); bool ValidImageSizeParameters(const Context *context, GLenum target, GLint level, @@ -42,7 +44,10 @@ bool ValidImageSizeParameters(const Context *context, GLsizei height, GLsizei depth, bool isSubImage); -bool ValidCompressedImageSize(const Context *context, GLenum internalFormat, GLsizei width, GLsizei height); +bool ValidCompressedImageSize(const ValidationContext *context, + GLenum internalFormat, + GLsizei width, + GLsizei height); bool ValidQueryType(const Context *context, GLenum queryType); // Returns valid program if id is a valid program name @@ -64,9 +69,17 @@ bool ValidateRenderbufferStorageParametersANGLE(Context *context, GLenum target, bool ValidateFramebufferRenderbufferParameters(Context *context, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); -bool ValidateBlitFramebufferParameters(Context *context, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, - GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, - GLenum filter, bool fromAngleExtension); +bool ValidateBlitFramebufferParameters(Context *context, + GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter); bool ValidateGetVertexAttribParameters(Context *context, GLenum pname); @@ -74,11 +87,40 @@ bool ValidateTexParamParameters(Context *context, GLenum pname, GLint param); bool ValidateSamplerObjectParameter(Context *context, GLenum pname); -bool ValidateReadPixelsParameters(Context *context, GLint x, GLint y, GLsizei width, GLsizei height, - GLenum format, GLenum type, GLsizei *bufSize, GLvoid *pixels); +bool ValidateReadPixels(Context *context, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLvoid *pixels); +bool ValidateReadnPixelsEXT(Context *context, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLsizei bufSize, + GLvoid *pixels); -bool ValidateBeginQuery(Context *context, GLenum target, GLuint id); -bool ValidateEndQuery(Context *context, GLenum target); +bool ValidateGenQueriesBase(gl::Context *context, GLsizei n, const GLuint *ids); +bool ValidateGenQueriesEXT(gl::Context *context, GLsizei n, const GLuint *ids); +bool ValidateDeleteQueriesBase(gl::Context *context, GLsizei n, const GLuint *ids); +bool ValidateDeleteQueriesEXT(gl::Context *context, GLsizei n, const GLuint *ids); +bool ValidateBeginQueryBase(Context *context, GLenum target, GLuint id); +bool ValidateBeginQueryEXT(Context *context, GLenum target, GLuint id); +bool ValidateEndQueryBase(Context *context, GLenum target); +bool ValidateEndQueryEXT(Context *context, GLenum target); +bool ValidateQueryCounterEXT(Context *context, GLuint id, GLenum target); +bool ValidateGetQueryivBase(Context *context, GLenum target, GLenum pname); +bool ValidateGetQueryivEXT(Context *context, GLenum target, GLenum pname, GLint *params); +bool ValidateGetQueryObjectValueBase(Context *context, GLenum target, GLenum pname); +bool ValidateGetQueryObjectivEXT(Context *context, GLuint id, GLenum pname, GLint *params); +bool ValidateGetQueryObjectuivEXT(Context *context, GLuint id, GLenum pname, GLuint *params); +bool ValidateGetQueryObjecti64vEXT(Context *context, GLuint id, GLenum pname, GLint64 *params); +bool ValidateGetQueryObjectui64vEXT(Context *context, GLuint id, GLenum pname, GLuint64 *params); bool ValidateUniform(Context *context, GLenum uniformType, GLint location, GLsizei count); bool ValidateUniformMatrix(Context *context, GLenum matrixType, GLint location, GLsizei count, @@ -86,9 +128,20 @@ bool ValidateUniformMatrix(Context *context, GLenum matrixType, GLint location, bool ValidateStateQuery(Context *context, GLenum pname, GLenum *nativeType, unsigned int *numParams); -bool ValidateCopyTexImageParametersBase(Context *context, GLenum target, GLint level, GLenum internalformat, bool isSubImage, - GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, - GLint border, GLenum *textureInternalFormatOut); +bool ValidateCopyTexImageParametersBase(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border, + GLenum *textureInternalFormatOut); bool ValidateDrawArrays(Context *context, GLenum mode, GLint first, GLsizei count, GLsizei primcount); bool ValidateDrawArraysInstanced(Context *context, GLenum mode, GLint first, GLsizei count, GLsizei primcount); @@ -121,6 +174,11 @@ bool ValidateFramebufferTextureBase(Context *context, GLenum target, GLenum atta GLuint texture, GLint level); bool ValidateFramebufferTexture2D(Context *context, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +bool ValidateFramebufferRenderbuffer(Context *context, + GLenum target, + GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer); bool ValidateGetUniformBase(Context *context, GLuint program, GLint location); bool ValidateGetUniformfv(Context *context, GLuint program, GLint location, GLfloat* params); @@ -147,8 +205,40 @@ bool ValidateBindVertexArrayBase(Context *context, GLuint array); bool ValidateDeleteVertexArraysBase(Context *context, GLsizei n); bool ValidateGenVertexArraysBase(Context *context, GLsizei n); +bool ValidateProgramBinaryBase(Context *context, + GLuint program, + GLenum binaryFormat, + const void *binary, + GLint length); +bool ValidateGetProgramBinaryBase(Context *context, + GLuint program, + GLsizei bufSize, + GLsizei *length, + GLenum *binaryFormat, + void *binary); + +bool ValidateCopyTexImage2D(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border); +bool ValidateDrawBuffersBase(ValidationContext *context, GLsizei n, const GLenum *bufs); +bool ValidateCopyTexSubImage2D(Context *context, + GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height); + // Error messages shared here for use in testing. extern const char *g_ExceedsMaxElementErrorMessage; -} +} // namespace gl #endif // LIBANGLE_VALIDATION_ES_H_ diff --git a/gfx/angle/src/libANGLE/validationES2.cpp b/gfx/angle/src/libANGLE/validationES2.cpp index fa1b1fd3a5..f5d43e9689 100644 --- a/gfx/angle/src/libANGLE/validationES2.cpp +++ b/gfx/angle/src/libANGLE/validationES2.cpp @@ -21,6 +21,42 @@ namespace gl { +namespace +{ + +bool IsPartialBlit(gl::Context *context, + const FramebufferAttachment *readBuffer, + const FramebufferAttachment *writeBuffer, + GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1) +{ + const Extents &writeSize = writeBuffer->getSize(); + const Extents &readSize = readBuffer->getSize(); + + if (srcX0 != 0 || srcY0 != 0 || dstX0 != 0 || dstY0 != 0 || dstX1 != writeSize.width || + dstY1 != writeSize.height || srcX1 != readSize.width || srcY1 != readSize.height) + { + return true; + } + + if (context->getState().isScissorTestEnabled()) + { + const Rectangle &scissor = context->getState().getScissor(); + return scissor.x > 0 || scissor.y > 0 || scissor.width < writeSize.width || + scissor.height < writeSize.height; + } + + return false; +} + +} // anonymous namespace + bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level, GLenum internalformat, bool isCompressed, bool isSubImage, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) @@ -159,6 +195,14 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level, return false; } break; + case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: + if (!context->getExtensions().lossyETCDecode) + { + context->recordError( + Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported")); + return false; + } + break; default: context->recordError(Error( GL_INVALID_ENUM, "internalformat is not a supported compressed internal format")); @@ -362,6 +406,21 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level, return false; } break; + case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: + if (context->getExtensions().lossyETCDecode) + { + context->recordError( + Error(GL_INVALID_OPERATION, + "ETC1_RGB8_LOSSY_DECODE_ANGLE can't work with this type.")); + return false; + } + else + { + context->recordError( + Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported.")); + return false; + } + break; case GL_DEPTH_COMPONENT: case GL_DEPTH_STENCIL_OES: if (!context->getExtensions().depthTextures) @@ -407,21 +466,34 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level, return true; } - - -bool ValidateES2CopyTexImageParameters(Context* context, GLenum target, GLint level, GLenum internalformat, bool isSubImage, - GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, +bool ValidateES2CopyTexImageParameters(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height, GLint border) { GLenum textureInternalFormat = GL_NONE; + if (!ValidTexture2DDestinationTarget(context, target)) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid texture target")); + return false; + } + if (!ValidateCopyTexImageParametersBase(context, target, level, internalformat, isSubImage, xoffset, yoffset, 0, x, y, width, height, border, &textureInternalFormat)) { return false; } - gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer(); + const gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer(); GLenum colorbufferFormat = framebuffer->getReadColorbuffer()->getInternalFormat(); const auto &internalFormatInfo = gl::GetInternalFormatInfo(textureInternalFormat); GLenum textureFormat = internalFormatInfo.format; @@ -521,6 +593,7 @@ bool ValidateES2CopyTexImageParameters(Context* context, GLenum target, GLint le case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE: case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE: case GL_ETC1_RGB8_OES: + case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: context->recordError(Error(GL_INVALID_OPERATION)); return false; case GL_DEPTH_COMPONENT: @@ -673,6 +746,20 @@ bool ValidateES2CopyTexImageParameters(Context* context, GLenum target, GLint le return false; } break; + case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: + if (context->getExtensions().lossyETCDecode) + { + context->recordError(Error(GL_INVALID_OPERATION, + "ETC1_RGB8_LOSSY_DECODE_ANGLE can't be copied to.")); + return false; + } + else + { + context->recordError( + Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported.")); + return false; + } + break; case GL_DEPTH_COMPONENT: case GL_DEPTH_COMPONENT16: case GL_DEPTH_COMPONENT32_OES: @@ -797,6 +884,14 @@ bool ValidateES2TexStorageParameters(Context *context, GLenum target, GLsizei le return false; } break; + case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: + if (!context->getExtensions().lossyETCDecode) + { + context->recordError( + Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported.")); + return false; + } + break; case GL_RGBA32F_EXT: case GL_RGB32F_EXT: case GL_ALPHA32F_EXT: @@ -920,6 +1015,12 @@ bool ValidES2ReadFormatType(Context *context, GLenum format, GLenum type) bool ValidateDiscardFramebufferEXT(Context *context, GLenum target, GLsizei numAttachments, const GLenum *attachments) { + if (!context->getExtensions().discardFramebuffer) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + bool defaultFramebuffer = false; switch (target) @@ -935,71 +1036,6 @@ bool ValidateDiscardFramebufferEXT(Context *context, GLenum target, GLsizei numA return ValidateDiscardFramebufferBase(context, target, numAttachments, attachments, defaultFramebuffer); } -bool ValidateDrawBuffers(Context *context, GLsizei n, const GLenum *bufs) -{ - // INVALID_VALUE is generated if n is negative or greater than value of MAX_DRAW_BUFFERS - if (n < 0 || static_cast(n) > context->getCaps().maxDrawBuffers) - { - context->recordError( - Error(GL_INVALID_VALUE, "n must be non-negative and no greater than MAX_DRAW_BUFFERS")); - return false; - } - - ASSERT(context->getState().getDrawFramebuffer()); - GLuint frameBufferId = context->getState().getDrawFramebuffer()->id(); - GLuint maxColorAttachment = GL_COLOR_ATTACHMENT0_EXT + context->getCaps().maxColorAttachments; - - // This should come first before the check for the default frame buffer - // because when we switch to ES3.1+, invalid enums will return INVALID_ENUM - // rather than INVALID_OPERATION - for (int colorAttachment = 0; colorAttachment < n; colorAttachment++) - { - const GLenum attachment = GL_COLOR_ATTACHMENT0_EXT + colorAttachment; - - if (bufs[colorAttachment] != GL_NONE && bufs[colorAttachment] != GL_BACK && - (bufs[colorAttachment] < GL_COLOR_ATTACHMENT0_EXT || - bufs[colorAttachment] >= maxColorAttachment)) - { - // Value in bufs is not NONE, BACK, or GL_COLOR_ATTACHMENTi - // In the 3.0 specs, the error should return GL_INVALID_OPERATION. - // When we move to 3.1 specs, we should change the error to be GL_INVALID_ENUM - context->recordError(Error(GL_INVALID_OPERATION, "Invalid buffer value")); - return false; - } - else if (bufs[colorAttachment] != GL_NONE && bufs[colorAttachment] != attachment && - frameBufferId != 0) - { - // INVALID_OPERATION-GL is bound to buffer and ith argument - // is not COLOR_ATTACHMENTi or NONE - context->recordError( - Error(GL_INVALID_OPERATION, "Ith value does not match COLOR_ATTACHMENTi or NONE")); - return false; - } - } - - // INVALID_OPERATION is generated if GL is bound to the default framebuffer - // and n is not 1 or bufs is bound to value other than BACK and NONE - if (frameBufferId == 0) - { - if (n != 1) - { - context->recordError(Error(GL_INVALID_OPERATION, - "n must be 1 when GL is bound to the default framebuffer")); - return false; - } - - if (bufs[0] != GL_NONE && bufs[0] != GL_BACK) - { - context->recordError(Error( - GL_INVALID_OPERATION, - "Only NONE or BACK are valid values when drawing to the default framebuffer")); - return false; - } - } - - return true; -} - bool ValidateBindVertexArrayOES(Context *context, GLuint array) { if (!context->getExtensions().vertexArrayObject) @@ -1043,4 +1079,684 @@ bool ValidateIsVertexArrayOES(Context *context) return true; } + +bool ValidateProgramBinaryOES(Context *context, + GLuint program, + GLenum binaryFormat, + const void *binary, + GLint length) +{ + if (!context->getExtensions().getProgramBinary) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + return ValidateProgramBinaryBase(context, program, binaryFormat, binary, length); } + +bool ValidateGetProgramBinaryOES(Context *context, + GLuint program, + GLsizei bufSize, + GLsizei *length, + GLenum *binaryFormat, + void *binary) +{ + if (!context->getExtensions().getProgramBinary) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + return ValidateGetProgramBinaryBase(context, program, bufSize, length, binaryFormat, binary); +} + +static bool ValidDebugSource(GLenum source, bool mustBeThirdPartyOrApplication) +{ + switch (source) + { + case GL_DEBUG_SOURCE_API: + case GL_DEBUG_SOURCE_SHADER_COMPILER: + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: + case GL_DEBUG_SOURCE_OTHER: + // Only THIRD_PARTY and APPLICATION sources are allowed to be manually inserted + return !mustBeThirdPartyOrApplication; + + case GL_DEBUG_SOURCE_THIRD_PARTY: + case GL_DEBUG_SOURCE_APPLICATION: + return true; + + default: + return false; + } +} + +static bool ValidDebugType(GLenum type) +{ + switch (type) + { + case GL_DEBUG_TYPE_ERROR: + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: + case GL_DEBUG_TYPE_PERFORMANCE: + case GL_DEBUG_TYPE_PORTABILITY: + case GL_DEBUG_TYPE_OTHER: + case GL_DEBUG_TYPE_MARKER: + case GL_DEBUG_TYPE_PUSH_GROUP: + case GL_DEBUG_TYPE_POP_GROUP: + return true; + + default: + return false; + } +} + +static bool ValidDebugSeverity(GLenum severity) +{ + switch (severity) + { + case GL_DEBUG_SEVERITY_HIGH: + case GL_DEBUG_SEVERITY_MEDIUM: + case GL_DEBUG_SEVERITY_LOW: + case GL_DEBUG_SEVERITY_NOTIFICATION: + return true; + + default: + return false; + } +} + +bool ValidateDebugMessageControlKHR(Context *context, + GLenum source, + GLenum type, + GLenum severity, + GLsizei count, + const GLuint *ids, + GLboolean enabled) +{ + if (!context->getExtensions().debug) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + if (!ValidDebugSource(source, false) && source != GL_DONT_CARE) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid debug source.")); + return false; + } + + if (!ValidDebugType(type) && type != GL_DONT_CARE) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid debug type.")); + return false; + } + + if (!ValidDebugSeverity(severity) && severity != GL_DONT_CARE) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid debug severity.")); + return false; + } + + if (count > 0) + { + if (source == GL_DONT_CARE || type == GL_DONT_CARE) + { + context->recordError(Error( + GL_INVALID_OPERATION, + "If count is greater than zero, source and severity cannot be GL_DONT_CARE.")); + return false; + } + + if (severity != GL_DONT_CARE) + { + context->recordError( + Error(GL_INVALID_OPERATION, + "If count is greater than zero, severity must be GL_DONT_CARE.")); + return false; + } + } + + return true; +} + +bool ValidateDebugMessageInsertKHR(Context *context, + GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar *buf) +{ + if (!context->getExtensions().debug) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + if (!context->getState().getDebug().isOutputEnabled()) + { + // If the DEBUG_OUTPUT state is disabled calls to DebugMessageInsert are discarded and do + // not generate an error. + return false; + } + + if (!ValidDebugSeverity(severity)) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid debug severity.")); + return false; + } + + if (!ValidDebugType(type)) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid debug type.")); + return false; + } + + if (!ValidDebugSource(source, true)) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid debug source.")); + return false; + } + + size_t messageLength = (length < 0) ? strlen(buf) : length; + if (messageLength > context->getExtensions().maxDebugMessageLength) + { + context->recordError( + Error(GL_INVALID_VALUE, "Message length is larger than GL_MAX_DEBUG_MESSAGE_LENGTH.")); + return false; + } + + return true; +} + +bool ValidateDebugMessageCallbackKHR(Context *context, + GLDEBUGPROCKHR callback, + const void *userParam) +{ + if (!context->getExtensions().debug) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + return true; +} + +bool ValidateGetDebugMessageLogKHR(Context *context, + GLuint count, + GLsizei bufSize, + GLenum *sources, + GLenum *types, + GLuint *ids, + GLenum *severities, + GLsizei *lengths, + GLchar *messageLog) +{ + if (!context->getExtensions().debug) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + if (bufSize < 0 && messageLog != nullptr) + { + context->recordError( + Error(GL_INVALID_VALUE, "bufSize must be positive if messageLog is not null.")); + return false; + } + + return true; +} + +bool ValidatePushDebugGroupKHR(Context *context, + GLenum source, + GLuint id, + GLsizei length, + const GLchar *message) +{ + if (!context->getExtensions().debug) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + if (!ValidDebugSource(source, true)) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid debug source.")); + return false; + } + + size_t messageLength = (length < 0) ? strlen(message) : length; + if (messageLength > context->getExtensions().maxDebugMessageLength) + { + context->recordError( + Error(GL_INVALID_VALUE, "Message length is larger than GL_MAX_DEBUG_MESSAGE_LENGTH.")); + return false; + } + + size_t currentStackSize = context->getState().getDebug().getGroupStackDepth(); + if (currentStackSize >= context->getExtensions().maxDebugGroupStackDepth) + { + context->recordError( + Error(GL_STACK_OVERFLOW, + "Cannot push more than GL_MAX_DEBUG_GROUP_STACK_DEPTH debug groups.")); + return false; + } + + return true; +} + +bool ValidatePopDebugGroupKHR(Context *context) +{ + if (!context->getExtensions().debug) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + size_t currentStackSize = context->getState().getDebug().getGroupStackDepth(); + if (currentStackSize <= 1) + { + context->recordError(Error(GL_STACK_UNDERFLOW, "Cannot pop the default debug group.")); + return false; + } + + return true; +} + +static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier, GLuint name) +{ + switch (identifier) + { + case GL_BUFFER: + if (context->getBuffer(name) == nullptr) + { + context->recordError(Error(GL_INVALID_VALUE, "name is not a valid buffer.")); + return false; + } + return true; + + case GL_SHADER: + if (context->getShader(name) == nullptr) + { + context->recordError(Error(GL_INVALID_VALUE, "name is not a valid shader.")); + return false; + } + return true; + + case GL_PROGRAM: + if (context->getProgram(name) == nullptr) + { + context->recordError(Error(GL_INVALID_VALUE, "name is not a valid program.")); + return false; + } + return true; + + case GL_VERTEX_ARRAY: + if (context->getVertexArray(name) == nullptr) + { + context->recordError(Error(GL_INVALID_VALUE, "name is not a valid vertex array.")); + return false; + } + return true; + + case GL_QUERY: + if (context->getQuery(name) == nullptr) + { + context->recordError(Error(GL_INVALID_VALUE, "name is not a valid query.")); + return false; + } + return true; + + case GL_TRANSFORM_FEEDBACK: + if (context->getTransformFeedback(name) == nullptr) + { + context->recordError( + Error(GL_INVALID_VALUE, "name is not a valid transform feedback.")); + return false; + } + return true; + + case GL_SAMPLER: + if (context->getSampler(name) == nullptr) + { + context->recordError(Error(GL_INVALID_VALUE, "name is not a valid sampler.")); + return false; + } + return true; + + case GL_TEXTURE: + if (context->getTexture(name) == nullptr) + { + context->recordError(Error(GL_INVALID_VALUE, "name is not a valid texture.")); + return false; + } + return true; + + case GL_RENDERBUFFER: + if (context->getRenderbuffer(name) == nullptr) + { + context->recordError(Error(GL_INVALID_VALUE, "name is not a valid renderbuffer.")); + return false; + } + return true; + + case GL_FRAMEBUFFER: + if (context->getFramebuffer(name) == nullptr) + { + context->recordError(Error(GL_INVALID_VALUE, "name is not a valid framebuffer.")); + return false; + } + return true; + + default: + context->recordError(Error(GL_INVALID_ENUM, "Invalid identifier.")); + return false; + } + + return true; +} + +bool ValidateObjectLabelKHR(Context *context, + GLenum identifier, + GLuint name, + GLsizei length, + const GLchar *label) +{ + if (!context->getExtensions().debug) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + if (!ValidateObjectIdentifierAndName(context, identifier, name)) + { + return false; + } + + size_t labelLength = (length < 0) ? strlen(label) : length; + if (labelLength > context->getExtensions().maxLabelLength) + { + context->recordError( + Error(GL_INVALID_VALUE, "Label length is larger than GL_MAX_LABEL_LENGTH.")); + return false; + } + + return true; +} + +bool ValidateGetObjectLabelKHR(Context *context, + GLenum identifier, + GLuint name, + GLsizei bufSize, + GLsizei *length, + GLchar *label) +{ + if (!context->getExtensions().debug) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + if (bufSize < 0) + { + context->recordError(Error(GL_INVALID_VALUE, "bufSize cannot be negative.")); + return false; + } + + if (!ValidateObjectIdentifierAndName(context, identifier, name)) + { + return false; + } + + // Can no-op if bufSize is zero. + return bufSize > 0; +} + +static bool ValidateObjectPtrName(Context *context, const void *ptr) +{ + if (context->getFenceSync(reinterpret_cast(const_cast(ptr))) == nullptr) + { + context->recordError(Error(GL_INVALID_VALUE, "name is not a valid sync.")); + return false; + } + + return true; +} + +bool ValidateObjectPtrLabelKHR(Context *context, + const void *ptr, + GLsizei length, + const GLchar *label) +{ + if (!context->getExtensions().debug) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + if (!ValidateObjectPtrName(context, ptr)) + { + return false; + } + + size_t labelLength = (length < 0) ? strlen(label) : length; + if (labelLength > context->getExtensions().maxLabelLength) + { + context->recordError( + Error(GL_INVALID_VALUE, "Label length is larger than GL_MAX_LABEL_LENGTH.")); + return false; + } + + return true; +} + +bool ValidateGetObjectPtrLabelKHR(Context *context, + const void *ptr, + GLsizei bufSize, + GLsizei *length, + GLchar *label) +{ + if (!context->getExtensions().debug) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + if (bufSize < 0) + { + context->recordError(Error(GL_INVALID_VALUE, "bufSize cannot be negative.")); + return false; + } + + if (!ValidateObjectPtrName(context, ptr)) + { + return false; + } + + // Can no-op if bufSize is zero. + return bufSize > 0; +} + +bool ValidateGetPointervKHR(Context *context, GLenum pname, void **params) +{ + if (!context->getExtensions().debug) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + // TODO: represent this in Context::getQueryParameterInfo. + switch (pname) + { + case GL_DEBUG_CALLBACK_FUNCTION: + case GL_DEBUG_CALLBACK_USER_PARAM: + break; + + default: + context->recordError(Error(GL_INVALID_ENUM, "Invalid pname.")); + return false; + } + + return true; +} + +bool ValidateBlitFramebufferANGLE(Context *context, + GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter) +{ + if (!context->getExtensions().framebufferBlit) + { + context->recordError(Error(GL_INVALID_OPERATION, "Blit extension not available.")); + return false; + } + + if (srcX1 - srcX0 != dstX1 - dstX0 || srcY1 - srcY0 != dstY1 - dstY0) + { + // TODO(jmadill): Determine if this should be available on other implementations. + context->recordError(Error( + GL_INVALID_OPERATION, + "Scaling and flipping in BlitFramebufferANGLE not supported by this implementation.")); + return false; + } + + if (filter == GL_LINEAR) + { + context->recordError(Error(GL_INVALID_ENUM, "Linear blit not supported in this extension")); + return false; + } + + const Framebuffer *readFramebuffer = context->getState().getReadFramebuffer(); + const Framebuffer *drawFramebuffer = context->getState().getDrawFramebuffer(); + + if (mask & GL_COLOR_BUFFER_BIT) + { + const FramebufferAttachment *readColorAttachment = readFramebuffer->getReadColorbuffer(); + const FramebufferAttachment *drawColorAttachment = drawFramebuffer->getFirstColorbuffer(); + + if (readColorAttachment && drawColorAttachment) + { + if (!(readColorAttachment->type() == GL_TEXTURE && + readColorAttachment->getTextureImageIndex().type == GL_TEXTURE_2D) && + readColorAttachment->type() != GL_RENDERBUFFER && + readColorAttachment->type() != GL_FRAMEBUFFER_DEFAULT) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + for (size_t drawbufferIdx = 0; + drawbufferIdx < drawFramebuffer->getDrawbufferStateCount(); ++drawbufferIdx) + { + const FramebufferAttachment *attachment = + drawFramebuffer->getDrawBuffer(drawbufferIdx); + if (attachment) + { + if (!(attachment->type() == GL_TEXTURE && + attachment->getTextureImageIndex().type == GL_TEXTURE_2D) && + attachment->type() != GL_RENDERBUFFER && + attachment->type() != GL_FRAMEBUFFER_DEFAULT) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + // Return an error if the destination formats do not match + if (attachment->getInternalFormat() != readColorAttachment->getInternalFormat()) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + } + } + + int readSamples = readFramebuffer->getSamples(context->getData()); + + if (readSamples != 0 && + IsPartialBlit(context, readColorAttachment, drawColorAttachment, srcX0, srcY0, + srcX1, srcY1, dstX0, dstY0, dstX1, dstY1)) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + } + } + + GLenum masks[] = {GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT}; + GLenum attachments[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT}; + for (size_t i = 0; i < 2; i++) + { + if (mask & masks[i]) + { + const FramebufferAttachment *readBuffer = + readFramebuffer->getAttachment(attachments[i]); + const FramebufferAttachment *drawBuffer = + drawFramebuffer->getAttachment(attachments[i]); + + if (readBuffer && drawBuffer) + { + if (IsPartialBlit(context, readBuffer, drawBuffer, srcX0, srcY0, srcX1, srcY1, + dstX0, dstY0, dstX1, dstY1)) + { + // only whole-buffer copies are permitted + ERR( + "Only whole-buffer depth and stencil blits are supported by this " + "implementation."); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + if (readBuffer->getSamples() != 0 || drawBuffer->getSamples() != 0) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + } + } + } + + return ValidateBlitFramebufferParameters(context, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, + dstX1, dstY1, mask, filter); +} + +bool ValidateClear(ValidationContext *context, GLbitfield mask) +{ + const Framebuffer *framebufferObject = context->getState().getDrawFramebuffer(); + ASSERT(framebufferObject); + + if (framebufferObject->checkStatus(context->getData()) != GL_FRAMEBUFFER_COMPLETE) + { + context->recordError(Error(GL_INVALID_FRAMEBUFFER_OPERATION)); + return false; + } + + if ((mask & ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) + { + context->recordError(Error(GL_INVALID_VALUE)); + return false; + } + + return true; +} + +bool ValidateDrawBuffersEXT(ValidationContext *context, GLsizei n, const GLenum *bufs) +{ + if (!context->getExtensions().drawBuffers) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not supported.")); + return false; + } + + return ValidateDrawBuffersBase(context, n, bufs); +} + +} // namespace gl diff --git a/gfx/angle/src/libANGLE/validationES2.h b/gfx/angle/src/libANGLE/validationES2.h index 44166af077..1b2cf13f60 100644 --- a/gfx/angle/src/libANGLE/validationES2.h +++ b/gfx/angle/src/libANGLE/validationES2.h @@ -10,18 +10,28 @@ #define LIBANGLE_VALIDATION_ES2_H_ #include +#include namespace gl { - class Context; +class ValidationContext; bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level, GLenum internalformat, bool isCompressed, bool isSubImage, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); -bool ValidateES2CopyTexImageParameters(Context* context, GLenum target, GLint level, GLenum internalformat, bool isSubImage, - GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, +bool ValidateES2CopyTexImageParameters(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height, GLint border); bool ValidateES2TexStorageParameters(Context *context, GLenum target, GLsizei levels, GLenum internalformat, @@ -32,12 +42,93 @@ bool ValidES2ReadFormatType(Context *context, GLenum format, GLenum type); bool ValidateDiscardFramebufferEXT(Context *context, GLenum target, GLsizei numAttachments, const GLenum *attachments); -bool ValidateDrawBuffers(Context *context, GLsizei n, const GLenum *bufs); +bool ValidateDrawBuffersEXT(ValidationContext *context, GLsizei n, const GLenum *bufs); bool ValidateBindVertexArrayOES(Context *context, GLuint array); bool ValidateDeleteVertexArraysOES(Context *context, GLsizei n); bool ValidateGenVertexArraysOES(Context *context, GLsizei n); bool ValidateIsVertexArrayOES(Context *context); -} + +bool ValidateProgramBinaryOES(Context *context, + GLuint program, + GLenum binaryFormat, + const void *binary, + GLint length); +bool ValidateGetProgramBinaryOES(Context *context, + GLuint program, + GLsizei bufSize, + GLsizei *length, + GLenum *binaryFormat, + void *binary); + +// GL_KHR_debug +bool ValidateDebugMessageControlKHR(Context *context, + GLenum source, + GLenum type, + GLenum severity, + GLsizei count, + const GLuint *ids, + GLboolean enabled); +bool ValidateDebugMessageInsertKHR(Context *context, + GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar *buf); +bool ValidateDebugMessageCallbackKHR(Context *context, + GLDEBUGPROCKHR callback, + const void *userParam); +bool ValidateGetDebugMessageLogKHR(Context *context, + GLuint count, + GLsizei bufSize, + GLenum *sources, + GLenum *types, + GLuint *ids, + GLenum *severities, + GLsizei *lengths, + GLchar *messageLog); +bool ValidatePushDebugGroupKHR(Context *context, + GLenum source, + GLuint id, + GLsizei length, + const GLchar *message); +bool ValidatePopDebugGroupKHR(Context *context); +bool ValidateObjectLabelKHR(Context *context, + GLenum identifier, + GLuint name, + GLsizei length, + const GLchar *label); +bool ValidateGetObjectLabelKHR(Context *context, + GLenum identifier, + GLuint name, + GLsizei bufSize, + GLsizei *length, + GLchar *label); +bool ValidateObjectPtrLabelKHR(Context *context, + const void *ptr, + GLsizei length, + const GLchar *label); +bool ValidateGetObjectPtrLabelKHR(Context *context, + const void *ptr, + GLsizei bufSize, + GLsizei *length, + GLchar *label); +bool ValidateGetPointervKHR(Context *context, GLenum pname, void **params); +bool ValidateBlitFramebufferANGLE(Context *context, + GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter); + +bool ValidateClear(ValidationContext *context, GLbitfield mask); + +} // namespace gl #endif // LIBANGLE_VALIDATION_ES2_H_ diff --git a/gfx/angle/src/libANGLE/validationES3.cpp b/gfx/angle/src/libANGLE/validationES3.cpp index 38395af9fe..e08e5d261b 100644 --- a/gfx/angle/src/libANGLE/validationES3.cpp +++ b/gfx/angle/src/libANGLE/validationES3.cpp @@ -202,12 +202,14 @@ ES3FormatCombinationSet BuildES3FormatSet() static bool ValidateTexImageFormatCombination(gl::Context *context, GLenum internalFormat, GLenum format, GLenum type) { - // Note: dEQP 2013.4 expects an INVALID_VALUE error for TexImage3D with an invalid - // internal format. (dEQP-GLES3.functional.negative_api.texture.teximage3d) + // For historical reasons, glTexImage2D and glTexImage3D pass in their internal format as a + // GLint instead of a GLenum. Therefor an invalid internal format gives a GL_INVALID_VALUE + // error instead of a GL_INVALID_ENUM error. As this validation function is only called in + // the validation codepaths for glTexImage2D/3D, we record a GL_INVALID_VALUE error. const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat); if (!formatInfo.textureSupport(context->getClientVersion(), context->getExtensions())) { - context->recordError(Error(GL_INVALID_ENUM)); + context->recordError(Error(GL_INVALID_VALUE)); return false; } @@ -260,16 +262,23 @@ static bool ValidateTexImageFormatCombination(gl::Context *context, GLenum inter return true; } -bool ValidateES3TexImageParameters(Context *context, GLenum target, GLint level, GLenum internalformat, bool isCompressed, bool isSubImage, - GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, - GLint border, GLenum format, GLenum type, const GLvoid *pixels) +bool ValidateES3TexImageParametersBase(Context *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isCompressed, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLint border, + GLenum format, + GLenum type, + const GLvoid *pixels) { - if (!ValidTexture2DDestinationTarget(context, target)) - { - context->recordError(Error(GL_INVALID_ENUM)); - return false; - } - // Validate image size if (!ValidImageSizeParameters(context, target, level, width, height, depth, isSubImage)) { @@ -502,6 +511,62 @@ bool ValidateES3TexImageParameters(Context *context, GLenum target, GLint level, return true; } +bool ValidateES3TexImage2DParameters(Context *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isCompressed, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLint border, + GLenum format, + GLenum type, + const GLvoid *pixels) +{ + if (!ValidTexture2DDestinationTarget(context, target)) + { + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + + return ValidateES3TexImageParametersBase(context, target, level, internalformat, isCompressed, + isSubImage, xoffset, yoffset, zoffset, width, height, + depth, border, format, type, pixels); +} + +bool ValidateES3TexImage3DParameters(Context *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isCompressed, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLint border, + GLenum format, + GLenum type, + const GLvoid *pixels) +{ + if (!ValidTexture3DDestinationTarget(context, target)) + { + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + + return ValidateES3TexImageParametersBase(context, target, level, internalformat, isCompressed, + isSubImage, xoffset, yoffset, zoffset, width, height, + depth, border, format, type, pixels); +} + struct EffectiveInternalFormatInfo { GLenum mEffectiveFormat; @@ -786,9 +851,19 @@ static bool IsValidES3CopyTexImageCombination(GLenum textureInternalFormat, GLen return false; } -bool ValidateES3CopyTexImageParameters(Context *context, GLenum target, GLint level, GLenum internalformat, - bool isSubImage, GLint xoffset, GLint yoffset, GLint zoffset, - GLint x, GLint y, GLsizei width, GLsizei height, GLint border) +bool ValidateES3CopyTexImageParametersBase(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border) { GLenum textureInternalFormat; if (!ValidateCopyTexImageParametersBase(context, target, level, internalformat, isSubImage, @@ -798,7 +873,9 @@ bool ValidateES3CopyTexImageParameters(Context *context, GLenum target, GLint le return false; } - gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer(); + const auto &state = context->getState(); + const gl::Framebuffer *framebuffer = state.getReadFramebuffer(); + GLuint readFramebufferID = framebuffer->id(); if (framebuffer->checkStatus(context->getData()) != GL_FRAMEBUFFER_COMPLETE) { @@ -806,8 +883,7 @@ bool ValidateES3CopyTexImageParameters(Context *context, GLenum target, GLint le return false; } - if (context->getState().getReadFramebuffer()->id() != 0 && - framebuffer->getSamples(context->getData()) != 0) + if (readFramebufferID != 0 && framebuffer->getSamples(context->getData()) != 0) { context->recordError(Error(GL_INVALID_OPERATION)); return false; @@ -819,7 +895,7 @@ bool ValidateES3CopyTexImageParameters(Context *context, GLenum target, GLint le if (isSubImage) { if (!IsValidES3CopyTexImageCombination(textureInternalFormat, colorbufferInternalFormat, - context->getState().getReadFramebuffer()->id())) + readFramebufferID)) { context->recordError(Error(GL_INVALID_OPERATION)); return false; @@ -828,7 +904,7 @@ bool ValidateES3CopyTexImageParameters(Context *context, GLenum target, GLint le else { if (!gl::IsValidES3CopyTexImageCombination(internalformat, colorbufferInternalFormat, - context->getState().getReadFramebuffer()->id())) + readFramebufferID)) { context->recordError(Error(GL_INVALID_OPERATION)); return false; @@ -839,8 +915,63 @@ bool ValidateES3CopyTexImageParameters(Context *context, GLenum target, GLint le return (width > 0 && height > 0); } -bool ValidateES3TexStorageParameters(Context *context, GLenum target, GLsizei levels, GLenum internalformat, - GLsizei width, GLsizei height, GLsizei depth) +bool ValidateES3CopyTexImage2DParameters(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border) +{ + if (!ValidTexture2DDestinationTarget(context, target)) + { + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + + return ValidateES3CopyTexImageParametersBase(context, target, level, internalformat, isSubImage, + xoffset, yoffset, zoffset, x, y, width, height, + border); +} + +bool ValidateES3CopyTexImage3DParameters(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border) +{ + if (!ValidTexture3DDestinationTarget(context, target)) + { + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + + return ValidateES3CopyTexImageParametersBase(context, target, level, internalformat, isSubImage, + xoffset, yoffset, zoffset, x, y, width, height, + border); +} + +bool ValidateES3TexStorageParametersBase(Context *context, + GLenum target, + GLsizei levels, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLsizei depth) { if (width < 1 || height < 1 || depth < 1 || levels < 1) { @@ -916,7 +1047,7 @@ bool ValidateES3TexStorageParameters(Context *context, GLenum target, GLsizei le break; default: - context->recordError(Error(GL_INVALID_ENUM)); + UNREACHABLE(); return false; } @@ -949,6 +1080,108 @@ bool ValidateES3TexStorageParameters(Context *context, GLenum target, GLsizei le return true; } +bool ValidateES3TexStorage2DParameters(Context *context, + GLenum target, + GLsizei levels, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLsizei depth) +{ + if (!ValidTexture2DTarget(context, target)) + { + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + + return ValidateES3TexStorageParametersBase(context, target, levels, internalformat, width, + height, depth); +} + +bool ValidateES3TexStorage3DParameters(Context *context, + GLenum target, + GLsizei levels, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLsizei depth) +{ + if (!ValidTexture3DTarget(context, target)) + { + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + + return ValidateES3TexStorageParametersBase(context, target, levels, internalformat, width, + height, depth); +} + +bool ValidateGenQueries(gl::Context *context, GLsizei n, const GLuint *ids) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION, "GLES version < 3.0")); + return false; + } + + return ValidateGenQueriesBase(context, n, ids); +} + +bool ValidateDeleteQueries(gl::Context *context, GLsizei n, const GLuint *ids) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION, "GLES version < 3.0")); + return false; + } + + return ValidateDeleteQueriesBase(context, n, ids); +} + +bool ValidateBeginQuery(gl::Context *context, GLenum target, GLuint id) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION, "GLES version < 3.0")); + return false; + } + + return ValidateBeginQueryBase(context, target, id); +} + +bool ValidateEndQuery(gl::Context *context, GLenum target) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION, "GLES version < 3.0")); + return false; + } + + return ValidateEndQueryBase(context, target); +} + +bool ValidateGetQueryiv(Context *context, GLenum target, GLenum pname, GLint *params) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION, "GLES version < 3.0")); + return false; + } + + return ValidateGetQueryivBase(context, target, pname); +} + +bool ValidateGetQueryObjectuiv(Context *context, GLuint id, GLenum pname, GLuint *params) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION, "GLES version < 3.0")); + return false; + } + + return ValidateGetQueryObjectValueBase(context, id, pname); +} + bool ValidateFramebufferTextureLayer(Context *context, GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) { @@ -1157,7 +1390,7 @@ bool ValidateInvalidateFramebuffer(Context *context, GLenum target, GLsizei numA return ValidateDiscardFramebufferBase(context, target, numAttachments, attachments, defaultFramebuffer); } -bool ValidateClearBuffer(Context *context) +bool ValidateClearBuffer(ValidationContext *context) { if (context->getClientVersion() < 3) { @@ -1272,8 +1505,8 @@ bool ValidateCompressedTexImage3D(Context *context, } // validateES3TexImageFormat sets the error code if there is an error - if (!ValidateES3TexImageParameters(context, target, level, internalformat, true, false, 0, 0, 0, - width, height, depth, border, GL_NONE, GL_NONE, data)) + if (!ValidateES3TexImage3DParameters(context, target, level, internalformat, true, false, 0, 0, + 0, width, height, depth, border, GL_NONE, GL_NONE, data)) { return false; } @@ -1324,4 +1557,228 @@ bool ValidateIsVertexArray(Context *context) return true; } + +bool ValidateProgramBinary(Context *context, + GLuint program, + GLenum binaryFormat, + const void *binary, + GLint length) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + return ValidateProgramBinaryBase(context, program, binaryFormat, binary, length); } + +bool ValidateGetProgramBinary(Context *context, + GLuint program, + GLsizei bufSize, + GLsizei *length, + GLenum *binaryFormat, + void *binary) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + return ValidateGetProgramBinaryBase(context, program, bufSize, length, binaryFormat, binary); +} + +bool ValidateProgramParameter(Context *context, GLuint program, GLenum pname, GLint value) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + if (GetValidProgram(context, program) == nullptr) + { + return false; + } + + switch (pname) + { + case GL_PROGRAM_BINARY_RETRIEVABLE_HINT: + break; + + default: + context->recordError(Error(GL_INVALID_ENUM, "Invalid pname: 0x%X", pname)); + return false; + } + + return true; +} + +bool ValidateBlitFramebuffer(Context *context, + GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + return ValidateBlitFramebufferParameters(context, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, + dstX1, dstY1, mask, filter); +} + +bool ValidateClearBufferiv(ValidationContext *context, + GLenum buffer, + GLint drawbuffer, + const GLint *value) +{ + switch (buffer) + { + case GL_COLOR: + if (drawbuffer < 0 || + static_cast(drawbuffer) >= context->getCaps().maxDrawBuffers) + { + context->recordError(Error(GL_INVALID_VALUE)); + return false; + } + break; + + case GL_STENCIL: + if (drawbuffer != 0) + { + context->recordError(Error(GL_INVALID_VALUE)); + return false; + } + break; + + default: + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + + return ValidateClearBuffer(context); +} + +bool ValidateClearBufferuiv(ValidationContext *context, + GLenum buffer, + GLint drawbuffer, + const GLuint *value) +{ + switch (buffer) + { + case GL_COLOR: + if (drawbuffer < 0 || + static_cast(drawbuffer) >= context->getCaps().maxDrawBuffers) + { + context->recordError(Error(GL_INVALID_VALUE)); + return false; + } + break; + + default: + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + + return ValidateClearBuffer(context); +} + +bool ValidateClearBufferfv(ValidationContext *context, + GLenum buffer, + GLint drawbuffer, + const GLfloat *value) +{ + switch (buffer) + { + case GL_COLOR: + if (drawbuffer < 0 || + static_cast(drawbuffer) >= context->getCaps().maxDrawBuffers) + { + context->recordError(Error(GL_INVALID_VALUE)); + return false; + } + break; + + case GL_DEPTH: + if (drawbuffer != 0) + { + context->recordError(Error(GL_INVALID_VALUE)); + return false; + } + break; + + default: + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + + return ValidateClearBuffer(context); +} + +bool ValidateClearBufferfi(ValidationContext *context, + GLenum buffer, + GLint drawbuffer, + GLfloat depth, + GLint stencil) +{ + switch (buffer) + { + case GL_DEPTH_STENCIL: + if (drawbuffer != 0) + { + context->recordError(Error(GL_INVALID_VALUE)); + return false; + } + break; + + default: + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + + return ValidateClearBuffer(context); +} + +bool ValidateDrawBuffers(ValidationContext *context, GLsizei n, const GLenum *bufs) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION, "Context does not support GLES3.")); + return false; + } + + return ValidateDrawBuffersBase(context, n, bufs); +} + +bool ValidateCopyTexSubImage3D(Context *context, + GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + return ValidateES3CopyTexImage3DParameters(context, target, level, GL_NONE, true, xoffset, + yoffset, zoffset, x, y, width, height, 0); +} + +} // namespace gl diff --git a/gfx/angle/src/libANGLE/validationES3.h b/gfx/angle/src/libANGLE/validationES3.h index c1d7b74f82..7bc657790a 100644 --- a/gfx/angle/src/libANGLE/validationES3.h +++ b/gfx/angle/src/libANGLE/validationES3.h @@ -13,19 +13,145 @@ namespace gl { - class Context; +class ValidationContext; -bool ValidateES3TexImageParameters(Context *context, GLenum target, GLint level, GLenum internalformat, bool isCompressed, bool isSubImage, - GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, - GLint border, GLenum format, GLenum type, const GLvoid *pixels); +bool ValidateES3TexImageParametersBase(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isCompressed, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLint border, + GLenum format, + GLenum type, + const GLvoid *pixels); -bool ValidateES3CopyTexImageParameters(Context *context, GLenum target, GLint level, GLenum internalformat, - bool isSubImage, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, - GLsizei width, GLsizei height, GLint border); +bool ValidateES3TexStorageParameters(Context *context, + GLenum target, + GLsizei levels, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLsizei depth); -bool ValidateES3TexStorageParameters(Context *context, GLenum target, GLsizei levels, GLenum internalformat, - GLsizei width, GLsizei height, GLsizei depth); +bool ValidateES3TexImage2DParameters(Context *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isCompressed, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLint border, + GLenum format, + GLenum type, + const GLvoid *pixels); + +bool ValidateES3TexImage3DParameters(Context *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isCompressed, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLint border, + GLenum format, + GLenum type, + const GLvoid *pixels); + +bool ValidateES3CopyTexImageParametersBase(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border); + +bool ValidateES3CopyTexImage2DParameters(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border); + +bool ValidateES3CopyTexImage3DParameters(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border); + +bool ValidateES3TexStorageParametersBase(Context *context, + GLenum target, + GLsizei levels, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLsizei depth); + +bool ValidateES3TexStorage2DParameters(Context *context, + GLenum target, + GLsizei levels, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLsizei depth); + +bool ValidateES3TexStorage3DParameters(Context *context, + GLenum target, + GLsizei levels, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLsizei depth); + +bool ValidateGenQueries(Context *context, GLsizei n, const GLuint *ids); + +bool ValidateDeleteQueries(Context *context, GLsizei n, const GLuint *ids); + +bool ValidateBeginQuery(Context *context, GLenum target, GLuint id); + +bool ValidateEndQuery(Context *context, GLenum target); + +bool ValidateGetQueryiv(Context *context, GLenum target, GLenum pname, GLint *params); + +bool ValidateGetQueryObjectuiv(Context *context, GLuint id, GLenum pname, GLuint *params); bool ValidateFramebufferTextureLayer(Context *context, GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); @@ -38,7 +164,7 @@ bool ValidateES3RenderbufferStorageParameters(Context *context, GLenum target, G bool ValidateInvalidateFramebuffer(Context *context, GLenum target, GLsizei numAttachments, const GLenum *attachments); -bool ValidateClearBuffer(Context *context); +bool ValidateClearBuffer(ValidationContext *context); bool ValidateGetUniformuiv(Context *context, GLuint program, GLint location, GLuint* params); @@ -59,6 +185,59 @@ bool ValidateBindVertexArray(Context *context, GLuint array); bool ValidateDeleteVertexArrays(Context *context, GLsizei n); bool ValidateGenVertexArrays(Context *context, GLsizei n); bool ValidateIsVertexArray(Context *context); -} + +bool ValidateProgramBinary(Context *context, + GLuint program, + GLenum binaryFormat, + const void *binary, + GLint length); +bool ValidateGetProgramBinary(Context *context, + GLuint program, + GLsizei bufSize, + GLsizei *length, + GLenum *binaryFormat, + void *binary); +bool ValidateProgramParameter(Context *context, GLuint program, GLenum pname, GLint value); +bool ValidateBlitFramebuffer(Context *context, + GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter); +bool ValidateClearBufferiv(ValidationContext *context, + GLenum buffer, + GLint drawbuffer, + const GLint *value); +bool ValidateClearBufferuiv(ValidationContext *context, + GLenum buffer, + GLint drawbuffer, + const GLuint *value); +bool ValidateClearBufferfv(ValidationContext *context, + GLenum buffer, + GLint drawbuffer, + const GLfloat *value); +bool ValidateClearBufferfi(ValidationContext *context, + GLenum buffer, + GLint drawbuffer, + GLfloat depth, + GLint stencil); +bool ValidateDrawBuffers(ValidationContext *context, GLsizei n, const GLenum *bufs); +bool ValidateCopyTexSubImage3D(Context *context, + GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height); + +} // namespace gl #endif // LIBANGLE_VALIDATION_ES3_H_ diff --git a/gfx/angle/src/libANGLE/validationES_unittest.cpp b/gfx/angle/src/libANGLE/validationES_unittest.cpp index 1b4a640bc2..ef049f99c2 100644 --- a/gfx/angle/src/libANGLE/validationES_unittest.cpp +++ b/gfx/angle/src/libANGLE/validationES_unittest.cpp @@ -20,6 +20,7 @@ using namespace gl; using namespace rx; using testing::_; +using testing::NiceMock; using testing::Return; namespace @@ -42,7 +43,8 @@ class MockValidationContext : public ValidationContext const TextureCapsMap &textureCaps, const Extensions &extensions, const ResourceManager *resourceManager, - const Limitations &limitations); + const Limitations &limitations, + bool skipValidation); MOCK_METHOD1(recordError, void(const Error &)); }; @@ -53,14 +55,16 @@ MockValidationContext::MockValidationContext(GLint clientVersion, const TextureCapsMap &textureCaps, const Extensions &extensions, const ResourceManager *resourceManager, - const Limitations &limitations) + const Limitations &limitations, + bool skipValidation) : ValidationContext(clientVersion, state, caps, textureCaps, extensions, resourceManager, - limitations) + limitations, + skipValidation) { } @@ -69,19 +73,11 @@ MockValidationContext::MockValidationContext(GLint clientVersion, // but we want a test to ensure we maintain this behaviour. TEST(ValidationESTest, DrawElementsWithMaxIndexGivesError) { + auto framebufferImpl = MakeFramebufferMock(); + auto programImpl = MakeProgramMock(); + // TODO(jmadill): Generalize some of this code so we can re-use it for other tests. - MockFramebufferImpl *framebufferImpl = new MockFramebufferImpl(); - EXPECT_CALL(*framebufferImpl, onUpdateColorAttachment(_)).Times(1).RetiresOnSaturation(); - EXPECT_CALL(*framebufferImpl, checkStatus()) - .Times(2) - .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE)) - .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE)); - EXPECT_CALL(*framebufferImpl, destroy()).Times(1).RetiresOnSaturation(); - - MockProgramImpl *programImpl = new MockProgramImpl(); - EXPECT_CALL(*programImpl, destroy()); - - MockFactory mockFactory; + NiceMock mockFactory; EXPECT_CALL(mockFactory, createFramebuffer(_)).WillOnce(Return(framebufferImpl)); EXPECT_CALL(mockFactory, createProgram(_)).WillOnce(Return(programImpl)); EXPECT_CALL(mockFactory, createVertexArray(_)).WillOnce(Return(nullptr)); @@ -96,11 +92,12 @@ TEST(ValidationESTest, DrawElementsWithMaxIndexGivesError) caps.maxElementIndex = 100; caps.maxDrawBuffers = 1; caps.maxColorAttachments = 1; - state.initialize(caps, 3); + state.initialize(caps, extensions, 3, false); - MockTextureImpl *textureImpl = new MockTextureImpl(); + NiceMock *textureImpl = new NiceMock(); EXPECT_CALL(*textureImpl, setStorage(_, _, _, _)).WillOnce(Return(Error(GL_NO_ERROR))); EXPECT_CALL(*textureImpl, destructor()).Times(1).RetiresOnSaturation(); + Texture *texture = new Texture(textureImpl, 0, GL_TEXTURE_2D); texture->addRef(); texture->setStorage(GL_TEXTURE_2D, 1, GL_RGBA8, Extents(1, 1, 0)); @@ -115,8 +112,8 @@ TEST(ValidationESTest, DrawElementsWithMaxIndexGivesError) state.setDrawFramebufferBinding(framebuffer); state.setProgram(program); - MockValidationContext testContext(3, state, caps, textureCaps, extensions, nullptr, - limitations); + NiceMock testContext(3, state, caps, textureCaps, extensions, nullptr, + limitations, false); // Set the expectation for the validation error here. Error expectedError(GL_INVALID_OPERATION, g_ExceedsMaxElementErrorMessage); diff --git a/gfx/angle/src/libEGL.gypi b/gfx/angle/src/libEGL.gypi index 7fc6b4afaa..1d8870452a 100644 --- a/gfx/angle/src/libEGL.gypi +++ b/gfx/angle/src/libEGL.gypi @@ -5,6 +5,10 @@ { # Everything below this is duplicated in the GN build. If you change # anything also change angle/BUILD.gn + 'variables': + { + 'angle_standalone%': 0, + }, 'targets': [ { @@ -33,14 +37,6 @@ ['angle_build_winrt==1', { 'msvs_requires_importlibrary' : 'true', - 'msvs_settings': - { - 'VCLinkerTool': - { - 'EnableCOMDATFolding': '1', - 'OptimizeReferences': '1', - } - }, }], ], }, diff --git a/gfx/angle/src/libEGL/libEGL.cpp b/gfx/angle/src/libEGL/libEGL.cpp index 405605f1a7..f28f40fcf5 100644 --- a/gfx/angle/src/libEGL/libEGL.cpp +++ b/gfx/angle/src/libEGL/libEGL.cpp @@ -271,6 +271,18 @@ EGLBoolean EGLAPIENTRY eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR image) return egl::DestroyImageKHR(dpy, image); } +EGLDeviceEXT EGLAPIENTRY eglCreateDeviceANGLE(EGLint device_type, + void *native_device, + const EGLAttrib *attrib_list) +{ + return egl::CreateDeviceANGLE(device_type, native_device, attrib_list); +} + +EGLBoolean EGLAPIENTRY eglReleaseDeviceANGLE(EGLDeviceEXT device) +{ + return egl::ReleaseDeviceANGLE(device); +} + __eglMustCastToProperFunctionPointerType EGLAPIENTRY eglGetProcAddress(const char *procname) { return egl::GetProcAddress(procname); diff --git a/gfx/angle/src/libEGL/libEGL.def b/gfx/angle/src/libEGL/libEGL.def index ddeb09f36e..74b7c2ae14 100644 --- a/gfx/angle/src/libEGL/libEGL.def +++ b/gfx/angle/src/libEGL/libEGL.def @@ -44,6 +44,8 @@ EXPORTS eglQueryDeviceStringEXT @50 eglCreateImageKHR @51 eglDestroyImageKHR @52 + eglCreateDeviceANGLE @53 + eglReleaseDeviceANGLE @54 ; 1.5 entry points eglCreateSync @38 diff --git a/gfx/angle/src/libEGL/moz.build b/gfx/angle/src/libEGL/moz.build index a0d33515f5..267a24f5ad 100644 --- a/gfx/angle/src/libEGL/moz.build +++ b/gfx/angle/src/libEGL/moz.build @@ -62,6 +62,8 @@ USE_LIBS += [ 'libGLESv2' ] DEFINES['LIBANGLE_IMPLEMENTATION'] = "1" DEFINES['ANGLE_ENABLE_HLSL'] = "1" +DEFINES['ANGLE_ENABLE_GLSL'] = "1" +DEFINES['ANGLE_ENABLE_ESSL'] = "1" DEFINES['ANGLE_ENABLE_KEYEDMUTEX'] = "1" if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']: diff --git a/gfx/angle/src/libGLESv2.gypi b/gfx/angle/src/libGLESv2.gypi index f4fcb313d6..a38693c47f 100644 --- a/gfx/angle/src/libGLESv2.gypi +++ b/gfx/angle/src/libGLESv2.gypi @@ -5,6 +5,8 @@ { 'variables': { + 'angle_standalone%': 0, + # These file lists are shared with the GN build. 'libangle_common_sources': [ @@ -68,6 +70,8 @@ 'libANGLE/Context.h', 'libANGLE/Data.cpp', 'libANGLE/Data.h', + 'libANGLE/Debug.cpp', + 'libANGLE/Debug.h', 'libANGLE/Device.cpp', 'libANGLE/Device.h', 'libANGLE/Display.cpp', @@ -94,7 +98,6 @@ 'libANGLE/Program.h', 'libANGLE/Query.cpp', 'libANGLE/Query.h', - 'libANGLE/RefCountObject.cpp', 'libANGLE/RefCountObject.h', 'libANGLE/Renderbuffer.cpp', 'libANGLE/Renderbuffer.h', @@ -265,6 +268,8 @@ 'libANGLE/renderer/d3d/d3d9/shaders/compiled/luminanceps.h', 'libANGLE/renderer/d3d/d3d9/shaders/compiled/passthroughps.h', 'libANGLE/renderer/d3d/d3d9/shaders/compiled/standardvs.h', + 'libANGLE/renderer/d3d/d3d9/StateManager9.cpp', + 'libANGLE/renderer/d3d/d3d9/StateManager9.h', 'libANGLE/renderer/d3d/d3d9/SwapChain9.cpp', 'libANGLE/renderer/d3d/d3d9/SwapChain9.h', 'libANGLE/renderer/d3d/d3d9/TextureStorage9.cpp', @@ -456,6 +461,8 @@ [ 'libANGLE/renderer/gl/wgl/DisplayWGL.cpp', 'libANGLE/renderer/gl/wgl/DisplayWGL.h', + 'libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.cpp', + 'libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.h', 'libANGLE/renderer/gl/wgl/FunctionsWGL.cpp', 'libANGLE/renderer/gl/wgl/FunctionsWGL.h', 'libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.cpp', @@ -475,6 +482,7 @@ 'libANGLE/renderer/gl/glx/FunctionsGLX.h', 'libANGLE/renderer/gl/glx/PbufferSurfaceGLX.cpp', 'libANGLE/renderer/gl/glx/PbufferSurfaceGLX.h', + 'libANGLE/renderer/gl/glx/SurfaceGLX.h', 'libANGLE/renderer/gl/glx/WindowSurfaceGLX.cpp', 'libANGLE/renderer/gl/glx/WindowSurfaceGLX.h', 'libANGLE/renderer/gl/glx/functionsglx_typedefs.h', @@ -733,10 +741,10 @@ ], 'link_settings': { 'ldflags': [ - '(display_id), AttributeMap()); } EGLBoolean EGLAPIENTRY Initialize(EGLDisplay dpy, EGLint *major, EGLint *minor) @@ -426,6 +426,37 @@ EGLBoolean EGLAPIENTRY QuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint a } *value = eglSurface->isFixedSize(); break; + case EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE: + if (!display->getExtensions().flexibleSurfaceCompatibility) + { + SetGlobalError( + Error(EGL_BAD_ATTRIBUTE, + "EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE cannot be used without " + "EGL_ANGLE_flexible_surface_compatibility support.")); + return EGL_FALSE; + } + *value = eglSurface->flexibleSurfaceCompatibilityRequested(); + break; + case EGL_SURFACE_ORIENTATION_ANGLE: + if (!display->getExtensions().surfaceOrientation) + { + SetGlobalError(Error(EGL_BAD_ATTRIBUTE, + "EGL_SURFACE_ORIENTATION_ANGLE cannot be queried without " + "EGL_ANGLE_surface_orientation support.")); + return EGL_FALSE; + } + *value = eglSurface->getOrientation(); + break; + case EGL_DIRECT_COMPOSITION_ANGLE: + if (!display->getExtensions().directComposition) + { + SetGlobalError(Error(EGL_BAD_ATTRIBUTE, + "EGL_DIRECT_COMPOSITION_ANGLE cannot be used without " + "EGL_ANGLE_direct_composition support.")); + return EGL_FALSE; + } + *value = eglSurface->directComposition(); + break; default: SetGlobalError(Error(EGL_BAD_ATTRIBUTE)); return EGL_FALSE; @@ -589,7 +620,9 @@ EGLBoolean EGLAPIENTRY MakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface r if (readSurface) { - Error readCompatError = ValidateCompatibleConfigs(readSurface->getConfig(), context->getConfig(), readSurface->getType()); + Error readCompatError = + ValidateCompatibleConfigs(display, readSurface->getConfig(), readSurface, + context->getConfig(), readSurface->getType()); if (readCompatError.isError()) { SetGlobalError(readCompatError); @@ -603,7 +636,9 @@ EGLBoolean EGLAPIENTRY MakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface r if (drawSurface) { - Error drawCompatError = ValidateCompatibleConfigs(drawSurface->getConfig(), context->getConfig(), drawSurface->getType()); + Error drawCompatError = + ValidateCompatibleConfigs(display, drawSurface->getConfig(), drawSurface, + context->getConfig(), drawSurface->getType()); if (drawCompatError.isError()) { SetGlobalError(drawCompatError); @@ -710,20 +745,56 @@ EGLBoolean EGLAPIENTRY WaitGL(void) { EVENT("()"); - UNIMPLEMENTED(); // FIXME + Display *display = GetGlobalDisplay(); + + Error error = ValidateDisplay(display); + if (error.isError()) + { + SetGlobalError(error); + return EGL_FALSE; + } + + // eglWaitGL like calling eglWaitClient with the OpenGL ES API bound. Since we only implement + // OpenGL ES we can do the call directly. + error = display->waitClient(); + if (error.isError()) + { + SetGlobalError(error); + return EGL_FALSE; + } SetGlobalError(Error(EGL_SUCCESS)); - return 0; + return EGL_TRUE; } EGLBoolean EGLAPIENTRY WaitNative(EGLint engine) { EVENT("(EGLint engine = %d)", engine); - UNIMPLEMENTED(); // FIXME + Display *display = GetGlobalDisplay(); + + Error error = ValidateDisplay(display); + if (error.isError()) + { + SetGlobalError(error); + return EGL_FALSE; + } + + if (engine != EGL_CORE_NATIVE_ENGINE) + { + SetGlobalError( + Error(EGL_BAD_PARAMETER, "the 'engine' parameter has an unrecognized value")); + } + + error = display->waitNative(engine, GetGlobalDrawSurface(), GetGlobalReadSurface()); + if (error.isError()) + { + SetGlobalError(error); + return EGL_FALSE; + } SetGlobalError(Error(EGL_SUCCESS)); - return 0; + return EGL_TRUE; } EGLBoolean EGLAPIENTRY SwapBuffers(EGLDisplay dpy, EGLSurface surface) @@ -1028,10 +1099,24 @@ EGLBoolean EGLAPIENTRY WaitClient(void) { EVENT("()"); - UNIMPLEMENTED(); // FIXME + Display *display = GetGlobalDisplay(); + + Error error = ValidateDisplay(display); + if (error.isError()) + { + SetGlobalError(error); + return EGL_FALSE; + } + + error = display->waitClient(); + if (error.isError()) + { + SetGlobalError(error); + return EGL_FALSE; + } SetGlobalError(Error(EGL_SUCCESS)); - return 0; + return EGL_TRUE; } // EGL 1.4 @@ -1361,6 +1446,19 @@ __eglMustCastToProperFunctionPointerType EGLAPIENTRY GetProcAddress(const char * INSERT_PROC_ADDRESS(gl, GenVertexArraysOES); INSERT_PROC_ADDRESS(gl, IsVertexArrayOES); + // GL_KHR_debug + INSERT_PROC_ADDRESS(gl, DebugMessageControlKHR); + INSERT_PROC_ADDRESS(gl, DebugMessageInsertKHR); + INSERT_PROC_ADDRESS(gl, DebugMessageCallbackKHR); + INSERT_PROC_ADDRESS(gl, GetDebugMessageLogKHR); + INSERT_PROC_ADDRESS(gl, PushDebugGroupKHR); + INSERT_PROC_ADDRESS(gl, PopDebugGroupKHR); + INSERT_PROC_ADDRESS(gl, ObjectLabelKHR); + INSERT_PROC_ADDRESS(gl, GetObjectLabelKHR); + INSERT_PROC_ADDRESS(gl, ObjectPtrLabelKHR); + INSERT_PROC_ADDRESS(gl, GetObjectPtrLabelKHR); + INSERT_PROC_ADDRESS(gl, GetPointervKHR); + // GLES3 core INSERT_PROC_ADDRESS(gl, ReadBuffer); INSERT_PROC_ADDRESS(gl, DrawRangeElements); @@ -1540,6 +1638,10 @@ __eglMustCastToProperFunctionPointerType EGLAPIENTRY GetProcAddress(const char * INSERT_PROC_ADDRESS(egl, CreateImageKHR); INSERT_PROC_ADDRESS(egl, DestroyImageKHR); + // EGL_EXT_device_creation + INSERT_PROC_ADDRESS(egl, CreateDeviceANGLE); + INSERT_PROC_ADDRESS(egl, ReleaseDeviceANGLE); + #undef INSERT_PROC_ADDRESS return map; }; diff --git a/gfx/angle/src/libGLESv2/entry_points_egl_ext.cpp b/gfx/angle/src/libGLESv2/entry_points_egl_ext.cpp index eb74fa3a91..7536b19fcb 100644 --- a/gfx/angle/src/libGLESv2/entry_points_egl_ext.cpp +++ b/gfx/angle/src/libGLESv2/entry_points_egl_ext.cpp @@ -143,144 +143,209 @@ EGLDisplay EGLAPIENTRY GetPlatformDisplayEXT(EGLenum platform, void *native_disp return EGL_NO_DISPLAY; } break; - + case EGL_PLATFORM_DEVICE_EXT: + if (!clientExtensions.platformDevice) + { + SetGlobalError(Error(EGL_BAD_PARAMETER, "Platform Device extension is not active")); + return EGL_NO_DISPLAY; + } + break; default: SetGlobalError(Error(EGL_BAD_CONFIG)); return EGL_NO_DISPLAY; } - EGLint platformType = EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE; - EGLint deviceType = EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE; - bool majorVersionSpecified = false; - bool minorVersionSpecified = false; - bool enableAutoTrimSpecified = false; - bool deviceTypeSpecified = false; - - if (attrib_list) + if (platform == EGL_PLATFORM_ANGLE_ANGLE) { - for (const EGLint *curAttrib = attrib_list; curAttrib[0] != EGL_NONE; curAttrib += 2) + EGLint platformType = EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE; + EGLint deviceType = EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE; + bool majorVersionSpecified = false; + bool minorVersionSpecified = false; + bool enableAutoTrimSpecified = false; + bool deviceTypeSpecified = false; + bool presentPathSpecified = false; + + if (attrib_list) { - switch (curAttrib[0]) + for (const EGLint *curAttrib = attrib_list; curAttrib[0] != EGL_NONE; curAttrib += 2) { - case EGL_PLATFORM_ANGLE_TYPE_ANGLE: - switch (curAttrib[1]) + switch (curAttrib[0]) { - case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE: - break; - - case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE: - case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE: - if (!clientExtensions.platformANGLED3D) + case EGL_PLATFORM_ANGLE_TYPE_ANGLE: + switch (curAttrib[1]) { + case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE: + break; + + case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE: + case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE: + if (!clientExtensions.platformANGLED3D) + { + SetGlobalError(Error(EGL_BAD_ATTRIBUTE)); + return EGL_NO_DISPLAY; + } + break; + + case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE: + case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE: + if (!clientExtensions.platformANGLEOpenGL) + { + SetGlobalError(Error(EGL_BAD_ATTRIBUTE)); + return EGL_NO_DISPLAY; + } + break; + + default: SetGlobalError(Error(EGL_BAD_ATTRIBUTE)); return EGL_NO_DISPLAY; } + platformType = curAttrib[1]; break; - case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE: - case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE: - if (!clientExtensions.platformANGLEOpenGL) + case EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE: + if (curAttrib[1] != EGL_DONT_CARE) + { + majorVersionSpecified = true; + } + break; + + case EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE: + if (curAttrib[1] != EGL_DONT_CARE) + { + minorVersionSpecified = true; + } + break; + + case EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE: + switch (curAttrib[1]) { + case EGL_TRUE: + case EGL_FALSE: + break; + default: SetGlobalError(Error(EGL_BAD_ATTRIBUTE)); return EGL_NO_DISPLAY; } + enableAutoTrimSpecified = true; break; - default: - SetGlobalError(Error(EGL_BAD_ATTRIBUTE)); - return EGL_NO_DISPLAY; - } - platformType = curAttrib[1]; - break; + case EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE: + if (!clientExtensions.experimentalPresentPath) + { + SetGlobalError( + Error(EGL_BAD_ATTRIBUTE, + "EGL_ANGLE_experimental_present_path extension not active")); + return EGL_NO_DISPLAY; + } - case EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE: - if (curAttrib[1] != EGL_DONT_CARE) - { - majorVersionSpecified = true; - } - break; + switch (curAttrib[1]) + { + case EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE: + case EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE: + break; + default: + SetGlobalError( + Error(EGL_BAD_ATTRIBUTE, + "Invalid value for EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE")); + return EGL_NO_DISPLAY; + } + presentPathSpecified = true; + break; - case EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE: - if (curAttrib[1] != EGL_DONT_CARE) - { - minorVersionSpecified = true; - } - break; + case EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE: + switch (curAttrib[1]) + { + case EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE: + case EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE: + case EGL_PLATFORM_ANGLE_DEVICE_TYPE_REFERENCE_ANGLE: + deviceTypeSpecified = true; + break; - case EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE: - switch (curAttrib[1]) - { - case EGL_TRUE: - case EGL_FALSE: - break; - default: - SetGlobalError(Error(EGL_BAD_ATTRIBUTE)); - return EGL_NO_DISPLAY; - } - enableAutoTrimSpecified = true; - break; + case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE: + // This is a hidden option, accepted by the OpenGL back-end. + break; - case EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE: - switch (curAttrib[1]) - { - case EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE: - case EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE: - case EGL_PLATFORM_ANGLE_DEVICE_TYPE_REFERENCE_ANGLE: - deviceTypeSpecified = true; + default: + SetGlobalError(Error(EGL_BAD_ATTRIBUTE, + "Invalid value for " + "EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE " + "attrib")); + return EGL_NO_DISPLAY; + } + deviceType = curAttrib[1]; break; - case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE: - // This is a hidden option, accepted by the OpenGL back-end. - break; - - default: - SetGlobalError(Error(EGL_BAD_ATTRIBUTE)); - return EGL_NO_DISPLAY; + default: + break; } - deviceType = curAttrib[1]; - break; - - default: - break; } } - } - if (!majorVersionSpecified && minorVersionSpecified) + if (!majorVersionSpecified && minorVersionSpecified) + { + SetGlobalError(Error(EGL_BAD_ATTRIBUTE)); + return EGL_NO_DISPLAY; + } + + if (deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE && + platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) + { + SetGlobalError( + Error(EGL_BAD_ATTRIBUTE, + "EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE requires a device type of " + "EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE.")); + return EGL_NO_DISPLAY; + } + + if (enableAutoTrimSpecified && platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) + { + SetGlobalError( + Error(EGL_BAD_ATTRIBUTE, + "EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE requires a device type of " + "EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE.")); + return EGL_NO_DISPLAY; + } + + if (presentPathSpecified && platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) + { + SetGlobalError(Error(EGL_BAD_ATTRIBUTE, + "EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE requires a device type of " + "EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE.")); + return EGL_NO_DISPLAY; + } + + if (deviceTypeSpecified && platformType != EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE && + platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) + { + SetGlobalError( + Error(EGL_BAD_ATTRIBUTE, + "EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE requires a device type of " + "EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE or EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE.")); + return EGL_NO_DISPLAY; + } + + SetGlobalError(Error(EGL_SUCCESS)); + return Display::GetDisplayFromAttribs(native_display, AttributeMap(attrib_list)); + } + else if (platform == EGL_PLATFORM_DEVICE_EXT) { - SetGlobalError(Error(EGL_BAD_ATTRIBUTE)); + Device *eglDevice = reinterpret_cast(native_display); + if (eglDevice == nullptr || !Device::IsValidDevice(eglDevice)) + { + SetGlobalError(Error(EGL_BAD_ATTRIBUTE, + "native_display should be a valid EGL device if platform equals " + "EGL_PLATFORM_DEVICE_EXT")); + return EGL_NO_DISPLAY; + } + + SetGlobalError(Error(EGL_SUCCESS)); + return Display::GetDisplayFromDevice(native_display); + } + else + { + UNREACHABLE(); return EGL_NO_DISPLAY; } - - if (deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE && - platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) - { - SetGlobalError(Error(EGL_BAD_ATTRIBUTE, "EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE requires a device type of " - "EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE.")); - return EGL_NO_DISPLAY; - } - - if (enableAutoTrimSpecified && - platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) - { - SetGlobalError(Error(EGL_BAD_ATTRIBUTE, "EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE requires a device type of " - "EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE.")); - return EGL_NO_DISPLAY; - } - - if (deviceTypeSpecified && - platformType != EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE && - platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) - { - SetGlobalError(Error(EGL_BAD_ATTRIBUTE, "EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE requires a device type of " - "EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE or EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE.")); - return EGL_NO_DISPLAY; - } - - SetGlobalError(Error(EGL_SUCCESS)); - - EGLNativeDisplayType displayId = static_cast(native_display); - return Display::getDisplay(displayId, AttributeMap(attrib_list)); } // EGL_EXT_device_query @@ -290,21 +355,25 @@ EGLBoolean EGLAPIENTRY QueryDeviceAttribEXT(EGLDeviceEXT device, EGLint attribut device, attribute, value); Device *dev = static_cast(device); - if (dev == EGL_NO_DEVICE_EXT) + if (dev == EGL_NO_DEVICE_EXT || !Device::IsValidDevice(dev)) { SetGlobalError(Error(EGL_BAD_ACCESS)); return EGL_FALSE; } - Display *display = dev->getDisplay(); + // If the device was created by (and is owned by) a display, and that display doesn't support + // device querying, then this call should fail + Display *owningDisplay = dev->getOwningDisplay(); + if (owningDisplay != nullptr && !owningDisplay->getExtensions().deviceQuery) + { + SetGlobalError(Error(EGL_BAD_ACCESS, + "Device wasn't created using eglCreateDeviceANGLE, and the Display " + "that created it doesn't support device querying")); + return EGL_FALSE; + } + Error error(EGL_SUCCESS); - if (!display->getExtensions().deviceQuery) - { - SetGlobalError(Error(EGL_BAD_ACCESS)); - return EGL_FALSE; - } - // validate the attribute parameter switch (attribute) { @@ -333,7 +402,7 @@ const char * EGLAPIENTRY QueryDeviceStringEXT(EGLDeviceEXT device, EGLint name) device, name); Device *dev = static_cast(device); - if (dev == EGL_NO_DEVICE_EXT) + if (dev == EGL_NO_DEVICE_EXT || !Device::IsValidDevice(dev)) { SetGlobalError(Error(EGL_BAD_DEVICE_EXT)); return nullptr; @@ -436,4 +505,50 @@ ANGLE_EXPORT EGLBoolean EGLAPIENTRY DestroyImageKHR(EGLDisplay dpy, EGLImageKHR return EGL_TRUE; } + +ANGLE_EXPORT EGLDeviceEXT EGLAPIENTRY CreateDeviceANGLE(EGLint device_type, + void *native_device, + const EGLAttrib *attrib_list) +{ + EVENT( + "(EGLint device_type = %d, void* native_device = 0x%0.8p, const EGLAttrib* attrib_list = " + "0x%0.8p)", + device_type, native_device, attrib_list); + + Error error = ValidateCreateDeviceANGLE(device_type, native_device, attrib_list); + if (error.isError()) + { + SetGlobalError(error); + return EGL_NO_DEVICE_EXT; + } + + Device *device = nullptr; + error = Device::CreateDevice(native_device, device_type, &device); + if (error.isError()) + { + ASSERT(device == nullptr); + SetGlobalError(error); + return EGL_NO_DEVICE_EXT; + } + + return device; +} + +ANGLE_EXPORT EGLBoolean EGLAPIENTRY ReleaseDeviceANGLE(EGLDeviceEXT device) +{ + EVENT("(EGLDeviceEXT device = 0x%0.8p)", device); + + Device *dev = static_cast(device); + + Error error = ValidateReleaseDeviceANGLE(dev); + if (error.isError()) + { + SetGlobalError(error); + return EGL_FALSE; + } + + SafeDelete(dev); + + return EGL_TRUE; +} } diff --git a/gfx/angle/src/libGLESv2/entry_points_egl_ext.h b/gfx/angle/src/libGLESv2/entry_points_egl_ext.h index aee52fdc21..d64fa6e483 100644 --- a/gfx/angle/src/libGLESv2/entry_points_egl_ext.h +++ b/gfx/angle/src/libGLESv2/entry_points_egl_ext.h @@ -37,6 +37,12 @@ ANGLE_EXPORT EGLImageKHR EGLAPIENTRY CreateImageKHR(EGLDisplay dpy, EGLClientBuffer buffer, const EGLint *attrib_list); ANGLE_EXPORT EGLBoolean EGLAPIENTRY DestroyImageKHR(EGLDisplay dpy, EGLImageKHR image); + +// EGL_EXT_device_creation +ANGLE_EXPORT EGLDeviceEXT EGLAPIENTRY CreateDeviceANGLE(EGLint device_type, + void *native_device, + const EGLAttrib *attrib_list); +ANGLE_EXPORT EGLBoolean EGLAPIENTRY ReleaseDeviceANGLE(EGLDeviceEXT device); } #endif // LIBGLESV2_ENTRYPOINTSEGLEXT_H_ diff --git a/gfx/angle/src/libGLESv2/entry_points_gles_2_0.cpp b/gfx/angle/src/libGLESv2/entry_points_gles_2_0.cpp index 2095bf2459..8d893df76a 100644 --- a/gfx/angle/src/libGLESv2/entry_points_gles_2_0.cpp +++ b/gfx/angle/src/libGLESv2/entry_points_gles_2_0.cpp @@ -593,27 +593,12 @@ void GL_APIENTRY Clear(GLbitfield mask) Context *context = GetValidGlobalContext(); if (context) { - Framebuffer *framebufferObject = context->getState().getDrawFramebuffer(); - ASSERT(framebufferObject); - - if (framebufferObject->checkStatus(context->getData()) != GL_FRAMEBUFFER_COMPLETE) + if (!context->skipValidation() && !ValidateClear(context, mask)) { - context->recordError(Error(GL_INVALID_FRAMEBUFFER_OPERATION)); return; } - if ((mask & ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) - { - context->recordError(Error(GL_INVALID_VALUE)); - return; - } - - Error error = framebufferObject->clear(context, mask); - if (error.isError()) - { - context->recordError(error); - return; - } + context->clear(mask); } } @@ -697,8 +682,9 @@ void GL_APIENTRY CompressedTexImage2D(GLenum target, GLint level, GLenum interna } if (context->getClientVersion() >= 3 && - !ValidateES3TexImageParameters(context, target, level, internalformat, true, false, - 0, 0, 0, width, height, 1, border, GL_NONE, GL_NONE, data)) + !ValidateES3TexImage2DParameters(context, target, level, internalformat, true, false, 0, + 0, 0, width, height, 1, border, GL_NONE, GL_NONE, + data)) { return; } @@ -742,8 +728,9 @@ void GL_APIENTRY CompressedTexSubImage2D(GLenum target, GLint level, GLint xoffs } if (context->getClientVersion() >= 3 && - !ValidateES3TexImageParameters(context, target, level, GL_NONE, true, true, - xoffset, yoffset, 0, width, height, 1, 0, GL_NONE, GL_NONE, data)) + !ValidateES3TexImage2DParameters(context, target, level, GL_NONE, true, true, xoffset, + yoffset, 0, width, height, 1, 0, GL_NONE, GL_NONE, + data)) { return; } @@ -777,30 +764,13 @@ void GL_APIENTRY CopyTexImage2D(GLenum target, GLint level, GLenum internalforma Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3 && - !ValidateES2CopyTexImageParameters(context, target, level, internalformat, false, - 0, 0, x, y, width, height, border)) + if (!context->skipValidation() && + !ValidateCopyTexImage2D(context, target, level, internalformat, x, y, width, height, + border)) { return; } - - if (context->getClientVersion() >= 3 && - !ValidateES3CopyTexImageParameters(context, target, level, internalformat, false, - 0, 0, 0, x, y, width, height, border)) - { - return; - } - - Rectangle sourceArea(x, y, width, height); - - const Framebuffer *framebuffer = context->getState().getReadFramebuffer(); - Texture *texture = context->getTargetTexture(IsCubeMapTextureTarget(target) ? GL_TEXTURE_CUBE_MAP : target); - Error error = texture->copyImage(target, level, sourceArea, internalformat, framebuffer); - if (error.isError()) - { - context->recordError(error); - return; - } + context->copyTexImage2D(target, level, internalformat, x, y, width, height, border); } } @@ -813,31 +783,14 @@ void GL_APIENTRY CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GL Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3 && - !ValidateES2CopyTexImageParameters(context, target, level, GL_NONE, true, - xoffset, yoffset, x, y, width, height, 0)) + if (!context->skipValidation() && + !ValidateCopyTexSubImage2D(context, target, level, xoffset, yoffset, x, y, width, + height)) { return; } - if (context->getClientVersion() >= 3 && - !ValidateES3CopyTexImageParameters(context, target, level, GL_NONE, true, - xoffset, yoffset, 0, x, y, width, height, 0)) - { - return; - } - - Offset destOffset(xoffset, yoffset, 0); - Rectangle sourceArea(x, y, width, height); - - const Framebuffer *framebuffer = context->getState().getReadFramebuffer(); - Texture *texture = context->getTargetTexture(IsCubeMapTextureTarget(target) ? GL_TEXTURE_CUBE_MAP : target); - Error error = texture->copySubImage(target, level, destOffset, sourceArea, framebuffer); - if (error.isError()) - { - context->recordError(error); - return; - } + context->copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); } } @@ -1287,29 +1240,14 @@ void GL_APIENTRY FramebufferRenderbuffer(GLenum target, GLenum attachment, GLenu Context *context = GetValidGlobalContext(); if (context) { - if (!ValidFramebufferTarget(target) || (renderbuffertarget != GL_RENDERBUFFER && renderbuffer != 0)) - { - context->recordError(Error(GL_INVALID_ENUM)); - return; - } - - if (!ValidateFramebufferRenderbufferParameters(context, target, attachment, renderbuffertarget, renderbuffer)) + if (!context->skipValidation() && + !ValidateFramebufferRenderbuffer(context, target, attachment, renderbuffertarget, + renderbuffer)) { return; } - Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target); - ASSERT(framebuffer); - - if (renderbuffer != 0) - { - Renderbuffer *renderbufferObject = context->getRenderbuffer(renderbuffer); - framebuffer->setAttachment(GL_RENDERBUFFER, attachment, gl::ImageIndex::MakeInvalid(), renderbufferObject); - } - else - { - framebuffer->resetAttachment(attachment); - } + context->framebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); } } @@ -1321,36 +1259,13 @@ void GL_APIENTRY FramebufferTexture2D(GLenum target, GLenum attachment, GLenum t Context *context = GetValidGlobalContext(); if (context) { - if (!ValidateFramebufferTexture2D(context, target, attachment, textarget, texture, level)) + if (!context->skipValidation() && + !ValidateFramebufferTexture2D(context, target, attachment, textarget, texture, level)) { return; } - Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target); - ASSERT(framebuffer); - - if (texture != 0) - { - Texture *textureObj = context->getTexture(texture); - - ImageIndex index = ImageIndex::MakeInvalid(); - - if (textarget == GL_TEXTURE_2D) - { - index = ImageIndex::Make2D(level); - } - else - { - ASSERT(IsCubeMapTextureTarget(textarget)); - index = ImageIndex::MakeCube(textarget, level); - } - - framebuffer->setAttachment(GL_TEXTURE, attachment, index, textureObj); - } - else - { - framebuffer->resetAttachment(attachment); - } + context->framebufferTexture2D(target, attachment, textarget, texture, level); } } @@ -2082,6 +1997,7 @@ void GL_APIENTRY GetProgramiv(GLuint program, GLenum pname, GLint* params) case GL_TRANSFORM_FEEDBACK_BUFFER_MODE: case GL_TRANSFORM_FEEDBACK_VARYINGS: case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH: + case GL_PROGRAM_BINARY_RETRIEVABLE_HINT: context->recordError(Error(GL_INVALID_ENUM)); return; } @@ -2134,6 +2050,9 @@ void GL_APIENTRY GetProgramiv(GLuint program, GLenum pname, GLint* params) case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH: *params = programObject->getTransformFeedbackVaryingMaxLength(); break; + case GL_PROGRAM_BINARY_RETRIEVABLE_HINT: + *params = programObject->getBinaryRetrievableHint(); + break; default: context->recordError(Error(GL_INVALID_ENUM)); @@ -3209,28 +3128,13 @@ void GL_APIENTRY ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, Context *context = GetValidGlobalContext(); if (context) { - if (width < 0 || height < 0) - { - context->recordError(Error(GL_INVALID_VALUE)); - return; - } - - if (!ValidateReadPixelsParameters(context, x, y, width, height, - format, type, NULL, pixels)) + if (!context->skipValidation() && + !ValidateReadPixels(context, x, y, width, height, format, type, pixels)) { return; } - Framebuffer *framebufferObject = context->getState().getReadFramebuffer(); - ASSERT(framebufferObject); - - Rectangle area(x, y, width, height); - Error error = framebufferObject->readPixels(context, area, format, type, pixels); - if (error.isError()) - { - context->recordError(error); - return; - } + context->readPixels(x, y, width, height, format, type, pixels); } } @@ -3544,8 +3448,9 @@ void GL_APIENTRY TexImage2D(GLenum target, GLint level, GLint internalformat, GL } if (context->getClientVersion() >= 3 && - !ValidateES3TexImageParameters(context, target, level, internalformat, false, false, - 0, 0, 0, width, height, 1, border, format, type, pixels)) + !ValidateES3TexImage2DParameters(context, target, level, internalformat, false, false, + 0, 0, 0, width, height, 1, border, format, type, + pixels)) { return; } @@ -3695,8 +3600,8 @@ void GL_APIENTRY TexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint } if (context->getClientVersion() >= 3 && - !ValidateES3TexImageParameters(context, target, level, GL_NONE, false, true, - xoffset, yoffset, 0, width, height, 1, 0, format, type, pixels)) + !ValidateES3TexImage2DParameters(context, target, level, GL_NONE, false, true, xoffset, + yoffset, 0, width, height, 1, 0, format, type, pixels)) { return; } diff --git a/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.cpp b/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.cpp index 1531ad0cab..7df6fcb0cd 100644 --- a/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.cpp +++ b/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.cpp @@ -27,6 +27,57 @@ namespace gl { +void GL_APIENTRY GenQueriesEXT(GLsizei n, GLuint *ids) +{ + EVENT("(GLsizei n = %d, GLuint* ids = 0x%0.8p)", n, ids); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateGenQueriesEXT(context, n, ids)) + { + return; + } + + for (GLsizei i = 0; i < n; i++) + { + ids[i] = context->createQuery(); + } + } +} + +void GL_APIENTRY DeleteQueriesEXT(GLsizei n, const GLuint *ids) +{ + EVENT("(GLsizei n = %d, const GLuint *ids = 0x%0.8p)", n, ids); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateDeleteQueriesEXT(context, n, ids)) + { + return; + } + + for (int i = 0; i < n; i++) + { + context->deleteQuery(ids[i]); + } + } +} + +GLboolean GL_APIENTRY IsQueryEXT(GLuint id) +{ + EVENT("(GLuint id = %d)", id); + + Context *context = GetValidGlobalContext(); + if (context) + { + return (context->getQuery(id, false, GL_NONE) != NULL) ? GL_TRUE : GL_FALSE; + } + + return GL_FALSE; +} + void GL_APIENTRY BeginQueryEXT(GLenum target, GLuint id) { EVENT("(GLenum target = 0x%X, GLuint %d)", target, id); @@ -34,7 +85,7 @@ void GL_APIENTRY BeginQueryEXT(GLenum target, GLuint id) Context *context = GetValidGlobalContext(); if (context) { - if (!ValidateBeginQuery(context, target, id)) + if (!ValidateBeginQueryEXT(context, target, id)) { return; } @@ -48,7 +99,150 @@ void GL_APIENTRY BeginQueryEXT(GLenum target, GLuint id) } } -void GL_APIENTRY DeleteFencesNV(GLsizei n, const GLuint* fences) +void GL_APIENTRY EndQueryEXT(GLenum target) +{ + EVENT("GLenum target = 0x%X)", target); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateEndQueryEXT(context, target)) + { + return; + } + + Error error = context->endQuery(target); + if (error.isError()) + { + context->recordError(error); + return; + } + } +} + +void GL_APIENTRY QueryCounterEXT(GLuint id, GLenum target) +{ + EVENT("GLuint id = %d, GLenum target = 0x%X)", id, target); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateQueryCounterEXT(context, id, target)) + { + return; + } + + Error error = context->queryCounter(id, target); + if (error.isError()) + { + context->recordError(error); + return; + } + } +} + +void GL_APIENTRY GetQueryivEXT(GLenum target, GLenum pname, GLint *params) +{ + EVENT("GLenum target = 0x%X, GLenum pname = 0x%X, GLint *params = 0x%0.8p)", target, pname, + params); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateGetQueryivEXT(context, target, pname, params)) + { + return; + } + + context->getQueryiv(target, pname, params); + } +} + +void GL_APIENTRY GetQueryObjectivEXT(GLuint id, GLenum pname, GLint *params) +{ + EVENT("(GLuint id = %d, GLenum pname = 0x%X, GLuint *params = 0x%0.8p)", id, pname, params); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateGetQueryObjectivEXT(context, id, pname, params)) + { + return; + } + + Error error = context->getQueryObjectiv(id, pname, params); + if (error.isError()) + { + context->recordError(error); + return; + } + } +} + +void GL_APIENTRY GetQueryObjectuivEXT(GLuint id, GLenum pname, GLuint *params) +{ + EVENT("(GLuint id = %d, GLenum pname = 0x%X, GLuint *params = 0x%0.8p)", id, pname, params); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateGetQueryObjectuivEXT(context, id, pname, params)) + { + return; + } + + Error error = context->getQueryObjectuiv(id, pname, params); + if (error.isError()) + { + context->recordError(error); + return; + } + } +} + +void GL_APIENTRY GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64 *params) +{ + EVENT("(GLuint id = %d, GLenum pname = 0x%X, GLuint *params = 0x%0.16p)", id, pname, params); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateGetQueryObjecti64vEXT(context, id, pname, params)) + { + return; + } + + Error error = context->getQueryObjecti64v(id, pname, params); + if (error.isError()) + { + context->recordError(error); + return; + } + } +} + +void GL_APIENTRY GetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64 *params) +{ + EVENT("(GLuint id = %d, GLenum pname = 0x%X, GLuint *params = 0x%0.16p)", id, pname, params); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateGetQueryObjectui64vEXT(context, id, pname, params)) + { + return; + } + + Error error = context->getQueryObjectui64v(id, pname, params); + if (error.isError()) + { + context->recordError(error); + return; + } + } +} + +void GL_APIENTRY DeleteFencesNV(GLsizei n, const GLuint *fences) { EVENT("(GLsizei n = %d, const GLuint* fences = 0x%0.8p)", n, fences); @@ -68,29 +262,13 @@ void GL_APIENTRY DeleteFencesNV(GLsizei n, const GLuint* fences) } } -void GL_APIENTRY DeleteQueriesEXT(GLsizei n, const GLuint *ids) +void GL_APIENTRY DrawArraysInstancedANGLE(GLenum mode, + GLint first, + GLsizei count, + GLsizei primcount) { - EVENT("(GLsizei n = %d, const GLuint *ids = 0x%0.8p)", n, ids); - - Context *context = GetValidGlobalContext(); - if (context) - { - if (n < 0) - { - context->recordError(Error(GL_INVALID_VALUE)); - return; - } - - for (int i = 0; i < n; i++) - { - context->deleteQuery(ids[i]); - } - } -} - -void GL_APIENTRY DrawArraysInstancedANGLE(GLenum mode, GLint first, GLsizei count, GLsizei primcount) -{ - EVENT("(GLenum mode = 0x%X, GLint first = %d, GLsizei count = %d, GLsizei primcount = %d)", mode, first, count, primcount); + EVENT("(GLenum mode = 0x%X, GLint first = %d, GLsizei count = %d, GLsizei primcount = %d)", + mode, first, count, primcount); Context *context = GetValidGlobalContext(); if (context) @@ -109,16 +287,23 @@ void GL_APIENTRY DrawArraysInstancedANGLE(GLenum mode, GLint first, GLsizei coun } } -void GL_APIENTRY DrawElementsInstancedANGLE(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount) +void GL_APIENTRY DrawElementsInstancedANGLE(GLenum mode, + GLsizei count, + GLenum type, + const GLvoid *indices, + GLsizei primcount) { - EVENT("(GLenum mode = 0x%X, GLsizei count = %d, GLenum type = 0x%X, const GLvoid* indices = 0x%0.8p, GLsizei primcount = %d)", - mode, count, type, indices, primcount); + EVENT( + "(GLenum mode = 0x%X, GLsizei count = %d, GLenum type = 0x%X, const GLvoid* indices = " + "0x%0.8p, GLsizei primcount = %d)", + mode, count, type, indices, primcount); Context *context = GetValidGlobalContext(); if (context) { IndexRange indexRange; - if (!ValidateDrawElementsInstancedANGLE(context, mode, count, type, indices, primcount, &indexRange)) + if (!ValidateDrawElementsInstancedANGLE(context, mode, count, type, indices, primcount, + &indexRange)) { return; } @@ -133,27 +318,6 @@ void GL_APIENTRY DrawElementsInstancedANGLE(GLenum mode, GLsizei count, GLenum t } } -void GL_APIENTRY EndQueryEXT(GLenum target) -{ - EVENT("GLenum target = 0x%X)", target); - - Context *context = GetValidGlobalContext(); - if (context) - { - if (!ValidateEndQuery(context, target)) - { - return; - } - - Error error = context->endQuery(target); - if (error.isError()) - { - context->recordError(error); - return; - } - } -} - void GL_APIENTRY FinishFenceNV(GLuint fence) { EVENT("(GLuint fence = %d)", fence); @@ -179,7 +343,7 @@ void GL_APIENTRY FinishFenceNV(GLuint fence) } } -void GL_APIENTRY GenFencesNV(GLsizei n, GLuint* fences) +void GL_APIENTRY GenFencesNV(GLsizei n, GLuint *fences) { EVENT("(GLsizei n = %d, GLuint* fences = 0x%0.8p)", n, fences); @@ -199,26 +363,6 @@ void GL_APIENTRY GenFencesNV(GLsizei n, GLuint* fences) } } -void GL_APIENTRY GenQueriesEXT(GLsizei n, GLuint* ids) -{ - EVENT("(GLsizei n = %d, GLuint* ids = 0x%0.8p)", n, ids); - - Context *context = GetValidGlobalContext(); - if (context) - { - if (n < 0) - { - context->recordError(Error(GL_INVALID_VALUE)); - return; - } - - for (GLsizei i = 0; i < n; i++) - { - ids[i] = context->createQuery(); - } - } -} - void GL_APIENTRY GetFenceivNV(GLuint fence, GLenum pname, GLint *params) { EVENT("(GLuint fence = %d, GLenum pname = 0x%X, GLint *params = 0x%0.8p)", fence, pname, params); @@ -290,84 +434,6 @@ GLenum GL_APIENTRY GetGraphicsResetStatusEXT(void) return GL_NO_ERROR; } -void GL_APIENTRY GetQueryivEXT(GLenum target, GLenum pname, GLint *params) -{ - EVENT("GLenum target = 0x%X, GLenum pname = 0x%X, GLint *params = 0x%0.8p)", target, pname, params); - - Context *context = GetValidGlobalContext(); - if (context) - { - if (!ValidQueryType(context, target)) - { - context->recordError(Error(GL_INVALID_ENUM)); - return; - } - - switch (pname) - { - case GL_CURRENT_QUERY_EXT: - params[0] = context->getState().getActiveQueryId(target); - break; - - default: - context->recordError(Error(GL_INVALID_ENUM)); - return; - } - } -} - -void GL_APIENTRY GetQueryObjectuivEXT(GLuint id, GLenum pname, GLuint *params) -{ - EVENT("(GLuint id = %d, GLenum pname = 0x%X, GLuint *params = 0x%0.8p)", id, pname, params); - - Context *context = GetValidGlobalContext(); - if (context) - { - Query *queryObject = context->getQuery(id, false, GL_NONE); - - if (!queryObject) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return; - } - - if (context->getState().getActiveQueryId(queryObject->getType()) == id) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return; - } - - switch(pname) - { - case GL_QUERY_RESULT_EXT: - { - Error error = queryObject->getResult(params); - if (error.isError()) - { - context->recordError(error); - return; - } - } - break; - - case GL_QUERY_RESULT_AVAILABLE_EXT: - { - Error error = queryObject->isResultAvailable(params); - if (error.isError()) - { - context->recordError(error); - return; - } - } - break; - - default: - context->recordError(Error(GL_INVALID_ENUM)); - return; - } - } -} - void GL_APIENTRY GetTranslatedShaderSourceANGLE(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source) { EVENT("(GLuint shader = %d, GLsizei bufsize = %d, GLsizei* length = 0x%0.8p, GLchar* source = 0x%0.8p)", @@ -456,19 +522,6 @@ GLboolean GL_APIENTRY IsFenceNV(GLuint fence) return GL_FALSE; } -GLboolean GL_APIENTRY IsQueryEXT(GLuint id) -{ - EVENT("(GLuint id = %d)", id); - - Context *context = GetValidGlobalContext(); - if (context) - { - return (context->getQuery(id, false, GL_NONE) != NULL) ? GL_TRUE : GL_FALSE; - } - - return GL_FALSE; -} - void GL_APIENTRY ReadnPixelsEXT(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, GLvoid *data) @@ -480,28 +533,13 @@ void GL_APIENTRY ReadnPixelsEXT(GLint x, GLint y, GLsizei width, GLsizei height, Context *context = GetValidGlobalContext(); if (context) { - if (width < 0 || height < 0 || bufSize < 0) - { - context->recordError(Error(GL_INVALID_VALUE)); - return; - } - - if (!ValidateReadPixelsParameters(context, x, y, width, height, - format, type, &bufSize, data)) + if (!context->skipValidation() && + !ValidateReadnPixelsEXT(context, x, y, width, height, format, type, bufSize, data)) { return; } - Framebuffer *framebufferObject = context->getState().getReadFramebuffer(); - ASSERT(framebufferObject); - - Rectangle area(x, y, width, height); - Error error = framebufferObject->readPixels(context, area, format, type, data); - if (error.isError()) - { - context->recordError(error); - return; - } + context->readPixels(x, y, width, height, format, type, data); } } @@ -615,7 +653,8 @@ void GL_APIENTRY TexStorage2DEXT(GLenum target, GLsizei levels, GLenum internalf } if (context->getClientVersion() >= 3 && - !ValidateES3TexStorageParameters(context, target, levels, internalformat, width, height, 1)) + !ValidateES3TexStorage2DParameters(context, target, levels, internalformat, width, + height, 1)) { return; } @@ -674,29 +713,15 @@ void GL_APIENTRY BlitFramebufferANGLE(GLint srcX0, GLint srcY0, GLint srcX1, GLi Context *context = GetValidGlobalContext(); if (context) { - if (!ValidateBlitFramebufferParameters(context, srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1, mask, filter, - true)) + if (!context->skipValidation() && + !ValidateBlitFramebufferANGLE(context, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, + dstY1, mask, filter)) { return; } - Framebuffer *readFramebuffer = context->getState().getReadFramebuffer(); - ASSERT(readFramebuffer); - - Framebuffer *drawFramebuffer = context->getState().getDrawFramebuffer(); - ASSERT(drawFramebuffer); - - Rectangle srcArea(srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0); - Rectangle dstArea(dstX0, dstY0, dstX1 - dstX0, dstY1 - dstY0); - - Error error = - drawFramebuffer->blit(context, srcArea, dstArea, mask, filter, readFramebuffer); - if (error.isError()) - { - context->recordError(error); - return; - } + context->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, + filter); } } @@ -707,28 +732,13 @@ void GL_APIENTRY DiscardFramebufferEXT(GLenum target, GLsizei numAttachments, co Context *context = GetValidGlobalContext(); if (context) { - if (!context->getExtensions().discardFramebuffer) - { - context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); - return; - } - - if (!ValidateDiscardFramebufferEXT(context, target, numAttachments, attachments)) + if (!context->skipValidation() && + !ValidateDiscardFramebufferEXT(context, target, numAttachments, attachments)) { return; } - Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target); - ASSERT(framebuffer); - - // The specification isn't clear what should be done when the framebuffer isn't complete. - // We leave it up to the framebuffer implementation to decide what to do. - Error error = framebuffer->discard(numAttachments, attachments); - if (error.isError()) - { - context->recordError(error); - return; - } + context->discardFramebuffer(target, numAttachments, attachments); } } @@ -751,14 +761,14 @@ void GL_APIENTRY GetProgramBinaryOES(GLuint program, GLsizei bufSize, GLsizei *l Context *context = GetValidGlobalContext(); if (context) { - Program *programObject = context->getProgram(program); - - if (!programObject || !programObject->isLinked()) + if (!ValidateGetProgramBinaryOES(context, program, bufSize, length, binaryFormat, binary)) { - context->recordError(Error(GL_INVALID_OPERATION)); return; } + Program *programObject = context->getProgram(program); + ASSERT(programObject != nullptr); + Error error = programObject->saveBinary(binaryFormat, binary, bufSize, length); if (error.isError()) { @@ -776,19 +786,13 @@ void GL_APIENTRY ProgramBinaryOES(GLuint program, GLenum binaryFormat, const voi Context *context = GetValidGlobalContext(); if (context) { - const std::vector &programBinaryFormats = context->getCaps().programBinaryFormats; - if (std::find(programBinaryFormats.begin(), programBinaryFormats.end(), binaryFormat) == programBinaryFormats.end()) + if (!ValidateProgramBinaryOES(context, program, binaryFormat, binary, length)) { - context->recordError(Error(GL_INVALID_ENUM)); return; } Program *programObject = context->getProgram(program); - if (!programObject) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return; - } + ASSERT(programObject != nullptr); Error error = programObject->loadBinary(binaryFormat, binary, length); if (error.isError()) @@ -806,15 +810,12 @@ void GL_APIENTRY DrawBuffersEXT(GLsizei n, const GLenum *bufs) Context *context = GetValidGlobalContext(); if (context) { - if (!ValidateDrawBuffers(context, n, bufs)) + if (!context->skipValidation() && !ValidateDrawBuffersEXT(context, n, bufs)) { return; } - Framebuffer *framebuffer = context->getState().getDrawFramebuffer(); - ASSERT(framebuffer); - - framebuffer->setDrawBuffers(n, bufs); + context->drawBuffers(n, bufs); } } @@ -1283,4 +1284,255 @@ GLboolean GL_APIENTRY IsVertexArrayOES(GLuint array) return GL_FALSE; } + +void GL_APIENTRY DebugMessageControlKHR(GLenum source, + GLenum type, + GLenum severity, + GLsizei count, + const GLuint *ids, + GLboolean enabled) +{ + EVENT( + "(GLenum source = 0x%X, GLenum type = 0x%X, GLenum severity = 0x%X, GLsizei count = %d, " + "GLint *ids = 0x%0.8p, GLboolean enabled = %d)", + source, type, severity, count, ids, enabled); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateDebugMessageControlKHR(context, source, type, severity, count, ids, enabled)) + { + return; + } + + std::vector idVector(ids, ids + count); + context->getState().getDebug().setMessageControl( + source, type, severity, std::move(idVector), (enabled != GL_FALSE)); + } +} + +void GL_APIENTRY DebugMessageInsertKHR(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar *buf) +{ + EVENT( + "(GLenum source = 0x%X, GLenum type = 0x%X, GLint id = %d, GLenum severity = 0x%X, GLsizei " + "length = %d, const GLchar *buf = 0x%0.8p)", + source, type, id, severity, length, buf); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateDebugMessageInsertKHR(context, source, type, id, severity, length, buf)) + { + return; + } + + std::string msg(buf, (length > 0) ? static_cast(length) : strlen(buf)); + context->getState().getDebug().insertMessage(source, type, id, severity, std::move(msg)); + } +} + +void GL_APIENTRY DebugMessageCallbackKHR(GLDEBUGPROCKHR callback, const void *userParam) +{ + EVENT("(GLDEBUGPROCKHR callback = 0x%0.8p, const void *userParam = 0x%0.8p)", callback, + userParam); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateDebugMessageCallbackKHR(context, callback, userParam)) + { + return; + } + + context->getState().getDebug().setCallback(callback, userParam); + } +} + +GLuint GL_APIENTRY GetDebugMessageLogKHR(GLuint count, + GLsizei bufSize, + GLenum *sources, + GLenum *types, + GLuint *ids, + GLenum *severities, + GLsizei *lengths, + GLchar *messageLog) +{ + EVENT( + "(GLsizei count = %d, GLsizei bufSize = %d, GLenum *sources, GLenum *types = 0x%0.8p, " + "GLuint *ids = 0x%0.8p, GLenum *severities = 0x%0.8p, GLsizei *lengths = 0x%0.8p, GLchar " + "*messageLog = 0x%0.8p)", + count, bufSize, sources, types, ids, severities, lengths, messageLog); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateGetDebugMessageLogKHR(context, count, bufSize, sources, types, ids, severities, + lengths, messageLog)) + { + return 0; + } + + return static_cast(context->getState().getDebug().getMessages( + count, bufSize, sources, types, ids, severities, lengths, messageLog)); + } + + return 0; +} + +void GL_APIENTRY PushDebugGroupKHR(GLenum source, GLuint id, GLsizei length, const GLchar *message) +{ + EVENT( + "(GLenum source = 0x%X, GLuint id = 0x%X, GLsizei length = %d, const GLchar *message = " + "0x%0.8p)", + source, id, length, message); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidatePushDebugGroupKHR(context, source, id, length, message)) + { + return; + } + + std::string msg(message, (length > 0) ? static_cast(length) : strlen(message)); + context->getState().getDebug().pushGroup(source, id, std::move(msg)); + } +} + +void GL_APIENTRY PopDebugGroupKHR(void) +{ + EVENT("()"); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidatePopDebugGroupKHR(context)) + { + return; + } + + context->getState().getDebug().popGroup(); + } +} + +void GL_APIENTRY ObjectLabelKHR(GLenum identifier, GLuint name, GLsizei length, const GLchar *label) +{ + EVENT( + "(GLenum identifier = 0x%X, GLuint name = %u, GLsizei length = %d, const GLchar *label = " + "0x%0.8p)", + identifier, name, length, label); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateObjectLabelKHR(context, identifier, name, length, label)) + { + return; + } + + LabeledObject *object = context->getLabeledObject(identifier, name); + ASSERT(object != nullptr); + + std::string lbl(label, (length > 0) ? static_cast(length) : strlen(label)); + object->setLabel(lbl); + } +} + +void GL_APIENTRY +GetObjectLabelKHR(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) +{ + EVENT( + "(GLenum identifier = 0x%X, GLuint name = %u, GLsizei bufSize = %d, GLsizei *length = " + "0x%0.8p, GLchar *label = 0x%0.8p)", + identifier, name, bufSize, length, label); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateGetObjectLabelKHR(context, identifier, name, bufSize, length, label)) + { + return; + } + + LabeledObject *object = context->getLabeledObject(identifier, name); + ASSERT(object != nullptr); + + const std::string &objectLabel = object->getLabel(); + size_t writeLength = std::min(static_cast(bufSize) - 1, objectLabel.length()); + std::copy(objectLabel.begin(), objectLabel.begin() + writeLength, label); + label[writeLength] = '\0'; + *length = static_cast(writeLength); + } +} + +void GL_APIENTRY ObjectPtrLabelKHR(const void *ptr, GLsizei length, const GLchar *label) +{ + EVENT("(const void *ptr = 0x%0.8p, GLsizei length = %d, const GLchar *label = 0x%0.8p)", ptr, + length, label); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateObjectPtrLabelKHR(context, ptr, length, label)) + { + return; + } + + LabeledObject *object = context->getLabeledObjectFromPtr(ptr); + ASSERT(object != nullptr); + + std::string lbl(label, (length > 0) ? static_cast(length) : strlen(label)); + object->setLabel(lbl); + } +} + +void GL_APIENTRY GetObjectPtrLabelKHR(const void *ptr, + GLsizei bufSize, + GLsizei *length, + GLchar *label) +{ + EVENT( + "(const void *ptr = 0x%0.8p, GLsizei bufSize = %d, GLsizei *length = 0x%0.8p, GLchar " + "*label = 0x%0.8p)", + ptr, bufSize, length, label); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateGetObjectPtrLabelKHR(context, ptr, bufSize, length, label)) + { + return; + } + + LabeledObject *object = context->getLabeledObjectFromPtr(ptr); + ASSERT(object != nullptr); + + const std::string &objectLabel = object->getLabel(); + size_t writeLength = std::min(static_cast(bufSize) - 1, objectLabel.length()); + std::copy(objectLabel.begin(), objectLabel.begin() + writeLength, label); + label[writeLength] = '\0'; + *length = static_cast(writeLength); + } +} + +void GL_APIENTRY GetPointervKHR(GLenum pname, void **params) +{ + EVENT("(GLenum pname = 0x%X, void **params = 0x%0.8p)", pname, params); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateGetPointervKHR(context, pname, params)) + { + return; + } + + context->getPointerv(pname, params); + } +} } diff --git a/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.h b/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.h index 486df6a199..a2fb9c5e80 100644 --- a/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.h +++ b/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.h @@ -55,6 +55,12 @@ ANGLE_EXPORT void GL_APIENTRY EndQueryEXT(GLenum target); ANGLE_EXPORT void GL_APIENTRY GetQueryivEXT(GLenum target, GLenum pname, GLint *params); ANGLE_EXPORT void GL_APIENTRY GetQueryObjectuivEXT(GLuint id, GLenum pname, GLuint *params); +// GL_EXT_disjoint_timer_query +ANGLE_EXPORT void GL_APIENTRY QueryCounterEXT(GLuint id, GLenum target); +ANGLE_EXPORT void GL_APIENTRY GetQueryObjectivEXT(GLuint id, GLenum pname, GLint *params); +ANGLE_EXPORT void GL_APIENTRY GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64 *params); +ANGLE_EXPORT void GL_APIENTRY GetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64 *params); + // GL_EXT_draw_buffers ANGLE_EXPORT void GL_APIENTRY DrawBuffersEXT(GLsizei n, const GLenum *bufs); @@ -91,6 +97,49 @@ ANGLE_EXPORT void GL_APIENTRY BindVertexArrayOES(GLuint array); ANGLE_EXPORT void GL_APIENTRY DeleteVertexArraysOES(GLsizei n, const GLuint *arrays); ANGLE_EXPORT void GL_APIENTRY GenVertexArraysOES(GLsizei n, GLuint *arrays); ANGLE_EXPORT GLboolean GL_APIENTRY IsVertexArrayOES(GLuint array); + +// GL_KHR_debug +ANGLE_EXPORT void GL_APIENTRY DebugMessageControlKHR(GLenum source, + GLenum type, + GLenum severity, + GLsizei count, + const GLuint *ids, + GLboolean enabled); +ANGLE_EXPORT void GL_APIENTRY DebugMessageInsertKHR(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar *buf); +ANGLE_EXPORT void GL_APIENTRY DebugMessageCallbackKHR(GLDEBUGPROCKHR callback, + const void *userParam); +ANGLE_EXPORT GLuint GL_APIENTRY GetDebugMessageLogKHR(GLuint count, + GLsizei bufSize, + GLenum *sources, + GLenum *types, + GLuint *ids, + GLenum *severities, + GLsizei *lengths, + GLchar *messageLog); +ANGLE_EXPORT void GL_APIENTRY PushDebugGroupKHR(GLenum source, + GLuint id, + GLsizei length, + const GLchar *message); +ANGLE_EXPORT void GL_APIENTRY PopDebugGroupKHR(void); +ANGLE_EXPORT void GL_APIENTRY ObjectLabelKHR(GLenum identifier, + GLuint name, + GLsizei length, + const GLchar *label); +ANGLE_EXPORT void GL_APIENTRY +GetObjectLabelKHR(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +ANGLE_EXPORT void GL_APIENTRY ObjectPtrLabelKHR(const void *ptr, + GLsizei length, + const GLchar *label); +ANGLE_EXPORT void GL_APIENTRY GetObjectPtrLabelKHR(const void *ptr, + GLsizei bufSize, + GLsizei *length, + GLchar *label); +ANGLE_EXPORT void GL_APIENTRY GetPointervKHR(GLenum pname, void **params); } #endif // LIBGLESV2_ENTRYPOINTGLES20EXT_H_ diff --git a/gfx/angle/src/libGLESv2/entry_points_gles_3_0.cpp b/gfx/angle/src/libGLESv2/entry_points_gles_3_0.cpp index 2c19eb6d58..856129aa07 100644 --- a/gfx/angle/src/libGLESv2/entry_points_gles_3_0.cpp +++ b/gfx/angle/src/libGLESv2/entry_points_gles_3_0.cpp @@ -35,13 +35,12 @@ void GL_APIENTRY ReadBuffer(GLenum mode) Context *context = GetValidGlobalContext(); if (context) { - if (!ValidateReadBuffer(context, mode)) + if (!context->skipValidation() && !ValidateReadBuffer(context, mode)) { return; } - Framebuffer *readFBO = context->getState().getReadFramebuffer(); - readFBO->setReadBuffer(mode); + context->readBuffer(mode); } } @@ -102,8 +101,9 @@ void GL_APIENTRY TexImage3D(GLenum target, GLint level, GLint internalformat, GL } // validateES3TexImageFormat sets the error code if there is an error - if (!ValidateES3TexImageParameters(context, target, level, internalformat, false, false, - 0, 0, 0, width, height, depth, border, format, type, pixels)) + if (!ValidateES3TexImage3DParameters(context, target, level, internalformat, false, false, + 0, 0, 0, width, height, depth, border, format, type, + pixels)) { return; } @@ -137,9 +137,9 @@ void GL_APIENTRY TexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint } // validateES3TexImageFormat sets the error code if there is an error - if (!ValidateES3TexImageParameters(context, target, level, GL_NONE, false, true, - xoffset, yoffset, zoffset, width, height, depth, 0, - format, type, pixels)) + if (!ValidateES3TexImage3DParameters(context, target, level, GL_NONE, false, true, xoffset, + yoffset, zoffset, width, height, depth, 0, format, + type, pixels)) { return; } @@ -171,29 +171,14 @@ void GL_APIENTRY CopyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GL Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return; - } - - if (!ValidateES3CopyTexImageParameters(context, target, level, GL_NONE, true, xoffset, yoffset, zoffset, - x, y, width, height, 0)) + if (!context->skipValidation() && + !ValidateCopyTexSubImage3D(context, target, level, xoffset, yoffset, zoffset, x, y, + width, height)) { return; } - Offset destOffset(xoffset, yoffset, zoffset); - Rectangle sourceArea(x, y, width, height); - - const Framebuffer *framebuffer = context->getState().getReadFramebuffer(); - Texture *texture = context->getTargetTexture(target); - Error error = texture->copySubImage(target, level, destOffset, sourceArea, framebuffer); - if (error.isError()) - { - context->recordError(error); - return; - } + context->copyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height); } } @@ -207,8 +192,8 @@ void GL_APIENTRY CompressedTexImage3D(GLenum target, GLint level, GLenum interna Context *context = GetValidGlobalContext(); if (context) { - if (!gl::ValidateCompressedTexImage3D(context, target, level, internalformat, width, height, - depth, border, imageSize, data)) + if (!ValidateCompressedTexImage3D(context, target, level, internalformat, width, height, + depth, border, imageSize, data)) { return; } @@ -256,8 +241,8 @@ void GL_APIENTRY CompressedTexSubImage3D(GLenum target, GLint level, GLint xoffs } // validateES3TexImageFormat sets the error code if there is an error - if (!ValidateES3TexImageParameters(context, target, level, GL_NONE, true, true, - 0, 0, 0, width, height, depth, 0, GL_NONE, GL_NONE, data)) + if (!ValidateES3TexImage3DParameters(context, target, level, GL_NONE, true, true, 0, 0, 0, + width, height, depth, 0, GL_NONE, GL_NONE, data)) { return; } @@ -288,15 +273,8 @@ void GL_APIENTRY GenQueries(GLsizei n, GLuint* ids) Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) + if (!ValidateGenQueries(context, n, ids)) { - context->recordError(Error(GL_INVALID_OPERATION)); - return; - } - - if (n < 0) - { - context->recordError(Error(GL_INVALID_VALUE)); return; } @@ -314,19 +292,12 @@ void GL_APIENTRY DeleteQueries(GLsizei n, const GLuint* ids) Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) + if (!ValidateDeleteQueries(context, n, ids)) { - context->recordError(Error(GL_INVALID_OPERATION)); return; } - if (n < 0) - { - context->recordError(Error(GL_INVALID_VALUE)); - return; - } - - for (GLsizei i = 0; i < n; i++) + for (int i = 0; i < n; i++) { context->deleteQuery(ids[i]); } @@ -359,12 +330,6 @@ void GL_APIENTRY BeginQuery(GLenum target, GLuint id) Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return; - } - if (!ValidateBeginQuery(context, target, id)) { return; @@ -386,12 +351,6 @@ void GL_APIENTRY EndQuery(GLenum target) Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return; - } - if (!ValidateEndQuery(context, target)) { return; @@ -413,28 +372,12 @@ void GL_APIENTRY GetQueryiv(GLenum target, GLenum pname, GLint* params) Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) + if (!ValidateGetQueryiv(context, target, pname, params)) { - context->recordError(Error(GL_INVALID_OPERATION)); return; } - if (!ValidQueryType(context, target)) - { - context->recordError(Error(GL_INVALID_ENUM)); - return; - } - - switch (pname) - { - case GL_CURRENT_QUERY: - params[0] = static_cast(context->getState().getActiveQueryId(target)); - break; - - default: - context->recordError(Error(GL_INVALID_ENUM)); - return; - } + context->getQueryiv(target, pname, params); } } @@ -445,52 +388,15 @@ void GL_APIENTRY GetQueryObjectuiv(GLuint id, GLenum pname, GLuint* params) Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) + if (!ValidateGetQueryObjectuiv(context, id, pname, params)) { - context->recordError(Error(GL_INVALID_OPERATION)); return; } - Query *queryObject = context->getQuery(id, false, GL_NONE); - - if (!queryObject) + Error error = context->getQueryObjectuiv(id, pname, params); + if (error.isError()) { - context->recordError(Error(GL_INVALID_OPERATION)); - return; - } - - if (context->getState().getActiveQueryId(queryObject->getType()) == id) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return; - } - - switch(pname) - { - case GL_QUERY_RESULT_EXT: - { - Error error = queryObject->getResult(params); - if (error.isError()) - { - context->recordError(error); - return; - } - } - break; - - case GL_QUERY_RESULT_AVAILABLE_EXT: - { - Error error = queryObject->isResultAvailable(params); - if (error.isError()) - { - context->recordError(error); - return; - } - } - break; - - default: - context->recordError(Error(GL_INVALID_ENUM)); + context->recordError(error); return; } } @@ -537,13 +443,12 @@ void GL_APIENTRY DrawBuffers(GLsizei n, const GLenum* bufs) Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) + if (!context->skipValidation() && !ValidateDrawBuffers(context, n, bufs)) { - context->recordError(Error(GL_INVALID_OPERATION)); return; } - DrawBuffersEXT(n, bufs); + context->drawBuffers(n, bufs); } } @@ -664,35 +569,15 @@ void GL_APIENTRY BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint sr Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return; - } - - if (!ValidateBlitFramebufferParameters(context, srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1, mask, filter, - false)) + if (!context->skipValidation() && + !ValidateBlitFramebuffer(context, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, + dstY1, mask, filter)) { return; } - Framebuffer *readFramebuffer = context->getState().getReadFramebuffer(); - ASSERT(readFramebuffer); - - Framebuffer *drawFramebuffer = context->getState().getDrawFramebuffer(); - ASSERT(drawFramebuffer); - - Rectangle srcArea(srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0); - Rectangle dstArea(dstX0, dstY0, dstX1 - dstX0, dstY1 - dstY0); - - Error error = - drawFramebuffer->blit(context, srcArea, dstArea, mask, filter, readFramebuffer); - if (error.isError()) - { - context->recordError(error); - return; - } + context->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, + filter); } } @@ -728,37 +613,13 @@ void GL_APIENTRY FramebufferTextureLayer(GLenum target, GLenum attachment, GLuin Context *context = GetValidGlobalContext(); if (context) { - if (!ValidateFramebufferTextureLayer(context, target, attachment, texture, - level, layer)) + if (!context->skipValidation() && + !ValidateFramebufferTextureLayer(context, target, attachment, texture, level, layer)) { return; } - Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target); - ASSERT(framebuffer); - - if (texture != 0) - { - Texture *textureObject = context->getTexture(texture); - - ImageIndex index = ImageIndex::MakeInvalid(); - - if (textureObject->getTarget() == GL_TEXTURE_3D) - { - index = ImageIndex::Make3D(level, layer); - } - else - { - ASSERT(textureObject->getTarget() == GL_TEXTURE_2D_ARRAY); - index = ImageIndex::Make2DArray(level, layer); - } - - framebuffer->setAttachment(GL_TEXTURE, attachment, index, textureObject); - } - else - { - framebuffer->resetAttachment(attachment); - } + context->framebufferTextureLayer(target, attachment, texture, level, layer); } } @@ -1674,43 +1535,13 @@ void GL_APIENTRY ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* val Context *context = GetValidGlobalContext(); if (context) { - if (!ValidateClearBuffer(context)) + if (!context->skipValidation() && + !ValidateClearBufferiv(context, buffer, drawbuffer, value)) { return; } - switch (buffer) - { - case GL_COLOR: - if (drawbuffer < 0 || static_cast(drawbuffer) >= context->getCaps().maxDrawBuffers) - { - context->recordError(Error(GL_INVALID_VALUE)); - return; - } - break; - - case GL_STENCIL: - if (drawbuffer != 0) - { - context->recordError(Error(GL_INVALID_VALUE)); - return; - } - break; - - default: - context->recordError(Error(GL_INVALID_ENUM)); - return; - } - - Framebuffer *framebufferObject = context->getState().getDrawFramebuffer(); - ASSERT(framebufferObject); - - Error error = framebufferObject->clearBufferiv(context, buffer, drawbuffer, value); - if (error.isError()) - { - context->recordError(error); - return; - } + context->clearBufferiv(buffer, drawbuffer, value); } } @@ -1722,35 +1553,13 @@ void GL_APIENTRY ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* v Context *context = GetValidGlobalContext(); if (context) { - if (!ValidateClearBuffer(context)) + if (!context->skipValidation() && + !ValidateClearBufferuiv(context, buffer, drawbuffer, value)) { return; } - switch (buffer) - { - case GL_COLOR: - if (drawbuffer < 0 || static_cast(drawbuffer) >= context->getCaps().maxDrawBuffers) - { - context->recordError(Error(GL_INVALID_VALUE)); - return; - } - break; - - default: - context->recordError(Error(GL_INVALID_ENUM)); - return; - } - - Framebuffer *framebufferObject = context->getState().getDrawFramebuffer(); - ASSERT(framebufferObject); - - Error error = framebufferObject->clearBufferuiv(context, buffer, drawbuffer, value); - if (error.isError()) - { - context->recordError(error); - return; - } + context->clearBufferuiv(buffer, drawbuffer, value); } } @@ -1762,43 +1571,13 @@ void GL_APIENTRY ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* v Context *context = GetValidGlobalContext(); if (context) { - if (!ValidateClearBuffer(context)) + if (!context->skipValidation() && + !ValidateClearBufferfv(context, buffer, drawbuffer, value)) { return; } - switch (buffer) - { - case GL_COLOR: - if (drawbuffer < 0 || static_cast(drawbuffer) >= context->getCaps().maxDrawBuffers) - { - context->recordError(Error(GL_INVALID_VALUE)); - return; - } - break; - - case GL_DEPTH: - if (drawbuffer != 0) - { - context->recordError(Error(GL_INVALID_VALUE)); - return; - } - break; - - default: - context->recordError(Error(GL_INVALID_ENUM)); - return; - } - - Framebuffer *framebufferObject = context->getState().getDrawFramebuffer(); - ASSERT(framebufferObject); - - Error error = framebufferObject->clearBufferfv(context, buffer, drawbuffer, value); - if (error.isError()) - { - context->recordError(error); - return; - } + context->clearBufferfv(buffer, drawbuffer, value); } } @@ -1810,41 +1589,13 @@ void GL_APIENTRY ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, G Context *context = GetValidGlobalContext(); if (context) { - if (!ValidateClearBuffer(context)) + if (!context->skipValidation() && + !ValidateClearBufferfi(context, buffer, drawbuffer, depth, stencil)) { return; } - switch (buffer) - { - case GL_DEPTH_STENCIL: - if (drawbuffer != 0) - { - context->recordError(Error(GL_INVALID_VALUE)); - return; - } - break; - - default: - context->recordError(Error(GL_INVALID_ENUM)); - return; - } - - Framebuffer *framebufferObject = context->getState().getDrawFramebuffer(); - ASSERT(framebufferObject); - - // If a buffer is not present, the clear has no effect - if (framebufferObject->getDepthbuffer() == nullptr && framebufferObject->getStencilbuffer() == nullptr) - { - return; - } - - Error error = framebufferObject->clearBufferfi(context, buffer, drawbuffer, depth, stencil); - if (error.isError()) - { - context->recordError(error); - return; - } + context->clearBufferfi(buffer, drawbuffer, depth, stencil); } } @@ -2924,9 +2675,11 @@ void GL_APIENTRY BindTransformFeedback(GLenum target, GLuint id) } // Cannot bind a transform feedback object that does not exist (3.0.2 pg 85 section 2.14.1) - if (context->getTransformFeedback(id) == NULL) + if (!context->isTransformFeedbackGenerated(id)) { - context->recordError(Error(GL_INVALID_OPERATION)); + context->recordError( + Error(GL_INVALID_OPERATION, + "Cannot bind a transform feedback object that does not exist.")); return; } @@ -2994,7 +2747,15 @@ GLboolean GL_APIENTRY IsTransformFeedback(GLuint id) return GL_FALSE; } - return ((context->getTransformFeedback(id) != NULL) ? GL_TRUE : GL_FALSE); + if (id == 0) + { + // The 3.0.4 spec [section 6.1.11] states that if ID is zero, IsTransformFeedback + // returns FALSE + return GL_FALSE; + } + + const TransformFeedback *transformFeedback = context->getTransformFeedback(id); + return ((transformFeedback != nullptr) ? GL_TRUE : GL_FALSE); } return GL_FALSE; @@ -3062,14 +2823,20 @@ void GL_APIENTRY GetProgramBinary(GLuint program, GLsizei bufSize, GLsizei* leng Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) + if (!ValidateGetProgramBinary(context, program, bufSize, length, binaryFormat, binary)) { - context->recordError(Error(GL_INVALID_OPERATION)); return; } - // TODO: Pipe through to the OES extension for now, needs proper validation - return GetProgramBinaryOES(program, bufSize, length, binaryFormat, binary); + Program *programObject = context->getProgram(program); + ASSERT(programObject != nullptr); + + Error error = programObject->saveBinary(binaryFormat, binary, bufSize, length); + if (error.isError()) + { + context->recordError(error); + return; + } } } @@ -3081,14 +2848,20 @@ void GL_APIENTRY ProgramBinary(GLuint program, GLenum binaryFormat, const GLvoid Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) + if (!ValidateProgramBinary(context, program, binaryFormat, binary, length)) { - context->recordError(Error(GL_INVALID_OPERATION)); return; } - // TODO: Pipe through to the OES extension for now, needs proper validation - return ProgramBinaryOES(program, binaryFormat, binary, length); + Program *programObject = context->getProgram(program); + ASSERT(programObject != nullptr); + + Error error = programObject->loadBinary(binaryFormat, binary, length); + if (error.isError()) + { + context->recordError(error); + return; + } } } @@ -3100,14 +2873,23 @@ void GL_APIENTRY ProgramParameteri(GLuint program, GLenum pname, GLint value) Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) + if (!ValidateProgramParameter(context, program, pname, value)) { - context->recordError(Error(GL_INVALID_OPERATION)); return; } - // glProgramParameteri - UNIMPLEMENTED(); + gl::Program *programObject = context->getProgram(program); + ASSERT(programObject != nullptr); + + switch (pname) + { + case GL_PROGRAM_BINARY_RETRIEVABLE_HINT: + programObject->setBinaryRetrievableHint(value != GL_FALSE); + break; + + default: + UNREACHABLE(); + } } } @@ -3119,23 +2901,13 @@ void GL_APIENTRY InvalidateFramebuffer(GLenum target, GLsizei numAttachments, co Context *context = GetValidGlobalContext(); if (context) { - if (!ValidateInvalidateFramebuffer(context, target, numAttachments, attachments)) + if (!context->skipValidation() && + !ValidateInvalidateFramebuffer(context, target, numAttachments, attachments)) { return; } - Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target); - ASSERT(framebuffer); - - if (framebuffer->checkStatus(context->getData()) == GL_FRAMEBUFFER_COMPLETE) - { - Error error = framebuffer->invalidate(numAttachments, attachments); - if (error.isError()) - { - context->recordError(error); - return; - } - } + context->invalidateFramebuffer(target, numAttachments, attachments); } } @@ -3148,24 +2920,13 @@ void GL_APIENTRY InvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, Context *context = GetValidGlobalContext(); if (context) { - if (!ValidateInvalidateFramebuffer(context, target, numAttachments, attachments)) + if (!context->skipValidation() && + !ValidateInvalidateFramebuffer(context, target, numAttachments, attachments)) { return; } - Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target); - ASSERT(framebuffer); - - if (framebuffer->checkStatus(context->getData()) == GL_FRAMEBUFFER_COMPLETE) - { - Rectangle area(x, y, width, height); - Error error = framebuffer->invalidateSub(numAttachments, attachments, area); - if (error.isError()) - { - context->recordError(error); - return; - } - } + context->invalidateSubFramebuffer(target, numAttachments, attachments, x, y, width, height); } } @@ -3183,7 +2944,8 @@ void GL_APIENTRY TexStorage2D(GLenum target, GLsizei levels, GLenum internalform return; } - if (!ValidateES3TexStorageParameters(context, target, levels, internalformat, width, height, 1)) + if (!ValidateES3TexStorage2DParameters(context, target, levels, internalformat, width, + height, 1)) { return; } @@ -3214,7 +2976,8 @@ void GL_APIENTRY TexStorage3D(GLenum target, GLsizei levels, GLenum internalform return; } - if (!ValidateES3TexStorageParameters(context, target, levels, internalformat, width, height, depth)) + if (!ValidateES3TexStorage3DParameters(context, target, levels, internalformat, width, + height, depth)) { return; } diff --git a/gfx/angle/src/libGLESv2/libGLESv2.cpp b/gfx/angle/src/libGLESv2/libGLESv2.cpp index 71eb25dd8a..42b749f373 100644 --- a/gfx/angle/src/libGLESv2/libGLESv2.cpp +++ b/gfx/angle/src/libGLESv2/libGLESv2.cpp @@ -1350,16 +1350,36 @@ void GL_APIENTRY glEndQueryEXT(GLenum target) return gl::EndQueryEXT(target); } +void GL_APIENTRY glQueryCounterEXT(GLuint id, GLenum target) +{ + return gl::QueryCounterEXT(id, target); +} + void GL_APIENTRY glGetQueryivEXT(GLenum target, GLenum pname, GLint *params) { return gl::GetQueryivEXT(target, pname, params); } +void GL_APIENTRY glGetQueryObjectivEXT(GLuint id, GLenum pname, GLint *params) +{ + return gl::GetQueryObjectivEXT(id, pname, params); +} + void GL_APIENTRY glGetQueryObjectuivEXT(GLuint id, GLenum pname, GLuint *params) { return gl::GetQueryObjectuivEXT(id, pname, params); } +void GL_APIENTRY glGetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64 *params) +{ + return gl::GetQueryObjecti64vEXT(id, pname, params); +} + +void GL_APIENTRY glGetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64 *params) +{ + return gl::GetQueryObjectui64vEXT(id, pname, params); +} + void GL_APIENTRY glDrawBuffersEXT(GLsizei n, const GLenum *bufs) { return gl::DrawBuffersEXT(n, bufs); @@ -1459,4 +1479,87 @@ GLboolean GL_APIENTRY glIsVertexArrayOES(GLuint array) { return gl::IsVertexArrayOES(array); } + +void GL_APIENTRY glDebugMessageControlKHR(GLenum source, + GLenum type, + GLenum severity, + GLsizei count, + const GLuint *ids, + GLboolean enabled) +{ + return gl::DebugMessageControlKHR(source, type, severity, count, ids, enabled); +} + +void GL_APIENTRY glDebugMessageInsertKHR(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar *buf) +{ + return gl::DebugMessageInsertKHR(source, type, id, severity, length, buf); +} + +void GL_APIENTRY glDebugMessageCallbackKHR(GLDEBUGPROCKHR callback, const void *userParam) +{ + return gl::DebugMessageCallbackKHR(callback, userParam); +} + +GLuint GL_APIENTRY glGetDebugMessageLogKHR(GLuint count, + GLsizei bufSize, + GLenum *sources, + GLenum *types, + GLuint *ids, + GLenum *severities, + GLsizei *lengths, + GLchar *messageLog) +{ + return gl::GetDebugMessageLogKHR(count, bufSize, sources, types, ids, severities, lengths, + messageLog); +} + +void GL_APIENTRY glPushDebugGroupKHR(GLenum source, + GLuint id, + GLsizei length, + const GLchar *message) +{ + return gl::PushDebugGroupKHR(source, id, length, message); +} + +void GL_APIENTRY glPopDebugGroupKHR(void) +{ + return gl::PopDebugGroupKHR(); +} + +void GL_APIENTRY glObjectLabelKHR(GLenum identifier, + GLuint name, + GLsizei length, + const GLchar *label) +{ + return gl::ObjectLabelKHR(identifier, name, length, label); +} + +void GL_APIENTRY +glGetObjectLabelKHR(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) +{ + return gl::GetObjectLabelKHR(identifier, name, bufSize, length, label); +} + +void GL_APIENTRY glObjectPtrLabelKHR(const void *ptr, GLsizei length, const GLchar *label) +{ + return gl::ObjectPtrLabelKHR(ptr, length, label); +} + +void GL_APIENTRY glGetObjectPtrLabelKHR(const void *ptr, + GLsizei bufSize, + GLsizei *length, + GLchar *label) +{ + return gl::GetObjectPtrLabelKHR(ptr, bufSize, length, label); +} + +void GL_APIENTRY glGetPointervKHR(GLenum pname, void **params) +{ + return gl::GetPointervKHR(pname, params); +} } diff --git a/gfx/angle/src/libGLESv2/libGLESv2.def b/gfx/angle/src/libGLESv2/libGLESv2.def index 6bba270426..d05987c151 100644 --- a/gfx/angle/src/libGLESv2/libGLESv2.def +++ b/gfx/angle/src/libGLESv2/libGLESv2.def @@ -187,6 +187,21 @@ EXPORTS glDeleteVertexArraysOES @300 glGenVertexArraysOES @301 glIsVertexArrayOES @302 + glDebugMessageControlKHR @303 + glDebugMessageInsertKHR @304 + glDebugMessageCallbackKHR @305 + glGetDebugMessageLogKHR @306 + glPushDebugGroupKHR @307 + glPopDebugGroupKHR @308 + glObjectLabelKHR @309 + glGetObjectLabelKHR @310 + glObjectPtrLabelKHR @311 + glGetObjectPtrLabelKHR @312 + glGetPointervKHR @313 + glQueryCounterEXT @314 + glGetQueryObjectivEXT @315 + glGetQueryObjecti64vEXT @316 + glGetQueryObjectui64vEXT @317 ; GLES 3.0 Functions glReadBuffer @180 diff --git a/gfx/angle/src/libGLESv2/moz.build b/gfx/angle/src/libGLESv2/moz.build index 2242b463f5..c693c72c83 100644 --- a/gfx/angle/src/libGLESv2/moz.build +++ b/gfx/angle/src/libGLESv2/moz.build @@ -68,6 +68,8 @@ LOCAL_INCLUDES += [ '../../include', '../../src', '../../src/third_party/khronos DEFINES['LIBANGLE_IMPLEMENTATION'] = "1" DEFINES['ANGLE_ENABLE_HLSL'] = "1" +DEFINES['ANGLE_ENABLE_GLSL'] = "1" +DEFINES['ANGLE_ENABLE_ESSL'] = "1" DEFINES['ANGLE_ENABLE_KEYEDMUTEX'] = "1" if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']: diff --git a/gfx/angle/src/tests/BUILD.gn b/gfx/angle/src/tests/BUILD.gn index f95a76e06f..f7bab8ac5f 100644 --- a/gfx/angle/src/tests/BUILD.gn +++ b/gfx/angle/src/tests/BUILD.gn @@ -2,8 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - import("//testing/test.gni") +import("//build/config/chromecast_build.gni") import("//third_party/angle/build/angle_common.gni") unittests_gypi = exec_script("//build/gypi_to_gn.py", @@ -56,11 +56,13 @@ if (is_win || is_linux || is_mac) { "../../util", ] - sources = rebase_path(end2end_gypi.angle_end2end_tests_sources, ".", "../..") + sources = + rebase_path(end2end_gypi.angle_end2end_tests_sources, ".", "../..") if (is_win) { - sources += - rebase_path(end2end_gypi.angle_end2end_tests_win_sources, ".", "../..") + sources += rebase_path(end2end_gypi.angle_end2end_tests_win_sources, + ".", + "../..") } sources += [ "//gpu/angle_end2end_tests_main.cc" ] @@ -70,6 +72,11 @@ if (is_win || is_linux || is_mac) { "//third_party/angle:libANGLE_config", ] + if (is_linux && !is_component_build) { + # Set rpath to find libEGL.so and libGLESv2.so even in a non-component build. + configs += [ "//build/config/gcc:rpath_for_built_shared_libraries" ] + } + deps = [ "//base", "//base/test:test_support", @@ -104,7 +111,7 @@ if (is_win) { sources = rebase_path(perftests_gypi.angle_perf_tests_sources, ".", "../..") sources += - rebase_path(perftests_gypi.angle_perf_tests_win_sources, ".", "../..") + rebase_path(perftests_gypi.angle_perf_tests_win_sources, ".", "../..") sources += [ "//gpu/angle_perftests_main.cc" ] @@ -125,3 +132,191 @@ if (is_win) { ] } } + +###----------------------------------------------------- +### dEQP tests +###----------------------------------------------------- + +declare_args() { + # Don't build dEQP by default. + build_angle_deqp_tests = false +} + +# TODO(jmadill): Other platforms. +if (build_angle_deqp_tests) { + deqp_gypi = exec_script("//build/gypi_to_gn.py", + [ + rebase_path("deqp.gypi"), + "--replace=<(angle_path)=.", + "--replace=<(deqp_path)=../deqp/src", + ], + "scope", + [ "deqp.gypi" ]) + + config("angle_deqp_support") { + include_dirs = rebase_path(deqp_gypi.deqp_include_dirs, ".", "../..") + if (is_win) { + include_dirs += [ "../deqp/src/framework/platform/win32" ] + cflags = deqp_gypi.deqp_win_cflags + } + defines = deqp_gypi.deqp_defines + defines += [ "_MBCS" ] + + if (is_linux) { + # Ask the system headers to expose all the regular function otherwise + # dEQP doesn't compile and produces warnings about implicitly defined + # functions. + # This has to be GNU_SOURCE as on Linux dEQP uses syscall() + defines += [ "_GNU_SOURCE" ] + } + } + + deqp_undefine_configs = [ + "//build/config/compiler:chromium_code", + "//build/config/compiler:no_rtti", + ] + + if (is_win) { + deqp_undefine_configs += [ + "//build/config/win:lean_and_mean", + "//build/config/win:nominmax", + "//build/config/win:unicode", + ] + } + + if (is_linux) { + deqp_undefine_configs += [ "//build/config/gcc:no_exceptions" ] + } + + static_library("angle_deqp_decpp") { + configs -= deqp_undefine_configs + public_configs = [ + ":angle_deqp_support", + "//build/config/compiler:no_chromium_code", + "//third_party/angle:internal_config", + ] + sources = rebase_path(deqp_gypi.deqp_libtester_decpp_sources, ".", "../..") + } + + config("angle_deqp_libtester_config") { + defines = [ "ANGLE_DEQP_LIBTESTER_IMPLEMENTATION" ] + + if (is_clang) { + # TODO(jmadill): Remove this once we fix dEQP. + cflags_cc = [ "-Wno-delete-non-virtual-dtor" ] + } + } + + static_library("angle_deqp_libtester") { + public_deps = [ + ":angle_deqp_decpp", + "//third_party/angle:angle_util", + "//third_party/angle:libEGL", + "//third_party/libpng:libpng", + ] + configs -= deqp_undefine_configs + public_configs = [ ":angle_deqp_libtester_config" ] + sources = rebase_path(deqp_gypi.deqp_libtester_sources, ".", "../..") + if (is_win) { + sources += rebase_path(deqp_gypi.deqp_libtester_sources_win, ".", "../..") + } + if (is_linux) { + sources += + rebase_path(deqp_gypi.deqp_libtester_sources_unix, ".", "../..") + } + } + + config("angle_deqp_gtest_support_config") { + include_dirs = [ "third_party/gpu_test_expectations" ] + } + + source_set("angle_deqp_gtest_support") { + testonly = true + public_deps = [ + "//base", + "//base/test:test_support", + "//testing/gtest", + "//third_party/angle:angle_common", + "//third_party/angle:angle_util", + ] + public_configs = [ ":angle_deqp_gtest_support_config" ] + sources = deqp_gypi.deqp_gpu_test_expectations_sources + sources += [ "//gpu/angle_deqp_tests_main.cc" ] + + # Taken from gpu/BUILD.gn + # TODO(jmadill): this should be in a shared location + if (is_linux && !is_chromecast) { + libs = [ "pci" ] + } + } + + api_names = [ + "gles2", + "gles3", + "egl", + ] + target_defines = [ + "ANGLE_DEQP_GLES2_TESTS", + "ANGLE_DEQP_GLES3_TESTS", + "ANGLE_DEQP_EGL_TESTS", + ] + target_sources = [ + deqp_gypi.deqp_gles2_sources, + deqp_gypi.deqp_gles3_sources, + deqp_gypi.deqp_egl_sources, + ] + + foreach(index, + [ + 0, + 1, + 2, + ]) { + api_name = api_names[index] + config_name = "angle_deqp_lib${api_name}_config" + config(config_name) { + defines = [ target_defines[index] ] + } + + shared_library_name = "angle_deqp_lib${api_name}" + shared_library(shared_library_name) { + deps = [ + ":angle_deqp_libtester", + ] + + configs -= deqp_undefine_configs + public_configs = [ ":${config_name}" ] + + sources = rebase_path(target_sources[index], ".", "../..") + sources += [ + "deqp_support/angle_deqp_libtester_main.cpp", + "deqp_support/tcuANGLEPlatform.cpp", + "deqp_support/tcuANGLEPlatform.h", + ] + } + + test_name = "angle_deqp_${api_name}_tests" + test(test_name) { + deps = [ + ":${shared_library_name}", + ":angle_deqp_gtest_support", + ] + + # Must be included outside of the source set for the define + sources = [ + "deqp_support/angle_deqp_gtest.cpp", + ] + + data = [ + "deqp_support/deqp_${api_name}_test_expectations.txt", + "../../../deqp/src/android/cts/master/${api_name}-master.txt", + "../../../deqp/src/data/", + ] + + if (is_linux && !is_component_build) { + # Set rpath to find *.so files even in a non-component build. + configs += [ "//build/config/gcc:rpath_for_built_shared_libraries" ] + } + } + } +} diff --git a/gfx/angle/src/tests/angle_end2end_tests.gypi b/gfx/angle/src/tests/angle_end2end_tests.gypi index 28ff23585a..2d9d87a08f 100644 --- a/gfx/angle/src/tests/angle_end2end_tests.gypi +++ b/gfx/angle/src/tests/angle_end2end_tests.gypi @@ -23,10 +23,12 @@ '<(angle_path)/src/tests/gl_tests/CopyTexImageTest.cpp', '<(angle_path)/src/tests/gl_tests/CubeMapTextureTest.cpp', '<(angle_path)/src/tests/gl_tests/DebugMarkerTest.cpp', + '<(angle_path)/src/tests/gl_tests/DebugTest.cpp', '<(angle_path)/src/tests/gl_tests/DepthStencilFormatsTest.cpp', '<(angle_path)/src/tests/gl_tests/DiscardFramebufferEXTTest.cpp', '<(angle_path)/src/tests/gl_tests/DrawBuffersTest.cpp', '<(angle_path)/src/tests/gl_tests/DrawElementsTest.cpp', + '<(angle_path)/src/tests/gl_tests/ETCTextureTest.cpp', '<(angle_path)/src/tests/gl_tests/FenceSyncTests.cpp', '<(angle_path)/src/tests/gl_tests/FramebufferFormatsTest.cpp', '<(angle_path)/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp', @@ -45,6 +47,7 @@ '<(angle_path)/src/tests/gl_tests/PBOExtensionTest.cpp', '<(angle_path)/src/tests/gl_tests/PointSpritesTest.cpp', '<(angle_path)/src/tests/gl_tests/ProvokingVertexTest.cpp', + '<(angle_path)/src/tests/gl_tests/ObjectAllocationTest.cpp', '<(angle_path)/src/tests/gl_tests/OcclusionQueriesTest.cpp', '<(angle_path)/src/tests/gl_tests/ProgramBinaryTest.cpp', '<(angle_path)/src/tests/gl_tests/ReadPixelsTest.cpp', @@ -52,8 +55,10 @@ '<(angle_path)/src/tests/gl_tests/SimpleOperationTest.cpp', '<(angle_path)/src/tests/gl_tests/SixteenBppTextureTest.cpp', '<(angle_path)/src/tests/gl_tests/SRGBTextureTest.cpp', + '<(angle_path)/src/tests/gl_tests/StateChangeTest.cpp', '<(angle_path)/src/tests/gl_tests/SwizzleTest.cpp', '<(angle_path)/src/tests/gl_tests/TextureTest.cpp', + '<(angle_path)/src/tests/gl_tests/TimerQueriesTest.cpp', '<(angle_path)/src/tests/gl_tests/TransformFeedbackTest.cpp', '<(angle_path)/src/tests/gl_tests/UniformBufferTest.cpp', '<(angle_path)/src/tests/gl_tests/UniformTest.cpp', @@ -77,10 +82,15 @@ '<(angle_path)/src/tests/gl_tests/D3D11EmulatedIndexedBufferTest.cpp', '<(angle_path)/src/tests/gl_tests/D3D11FormatTablesTest.cpp', '<(angle_path)/src/tests/gl_tests/D3D11InputLayoutCacheTest.cpp', - '<(angle_path)/src/tests/gl_tests/QueryDisplayAttribTest.cpp', + '<(angle_path)/src/tests/egl_tests/EGLDeviceTest.cpp', + '<(angle_path)/src/tests/egl_tests/EGLPresentPathD3D11Test.cpp', # TODO(cwallez) for Linux, requires a portable implementation of threads '<(angle_path)/src/tests/egl_tests/EGLThreadTest.cpp', ], + 'angle_end2end_tests_x11_sources': + [ + '<(angle_path)/src/tests/egl_tests/EGLX11VisualTest.cpp', + ], }, 'dependencies': [ @@ -108,5 +118,12 @@ '<@(angle_end2end_tests_win_sources)', ], }], + ['use_x11==1', + { + 'sources': + [ + '<@(angle_end2end_tests_x11_sources)', + ], + }], ] } diff --git a/gfx/angle/src/tests/angle_perftests.gypi b/gfx/angle/src/tests/angle_perftests.gypi index 16bc990692..8446ecde2c 100644 --- a/gfx/angle/src/tests/angle_perftests.gypi +++ b/gfx/angle/src/tests/angle_perftests.gypi @@ -21,9 +21,11 @@ '<(angle_path)/src/tests/perf_tests/DrawCallPerf.cpp', '<(angle_path)/src/tests/perf_tests/EGLInitializePerf.cpp', '<(angle_path)/src/tests/perf_tests/IndexConversionPerf.cpp', + '<(angle_path)/src/tests/perf_tests/InstancingPerf.cpp', '<(angle_path)/src/tests/perf_tests/InterleavedAttributeData.cpp', '<(angle_path)/src/tests/perf_tests/PointSprites.cpp', '<(angle_path)/src/tests/perf_tests/TexSubImage.cpp', + '<(angle_path)/src/tests/perf_tests/TextureSampling.cpp', '<(angle_path)/src/tests/perf_tests/third_party/perf/perf_test.cc', '<(angle_path)/src/tests/perf_tests/third_party/perf/perf_test.h', '<(angle_path)/src/tests/test_utils/angle_test_configs.cpp', diff --git a/gfx/angle/src/tests/angle_unittests.gypi b/gfx/angle/src/tests/angle_unittests.gypi index bd7253bc02..2a5f5719f0 100644 --- a/gfx/angle/src/tests/angle_unittests.gypi +++ b/gfx/angle/src/tests/angle_unittests.gypi @@ -20,6 +20,7 @@ '<(angle_path)/src/common/matrix_utils_unittest.cpp', '<(angle_path)/src/common/string_utils_unittest.cpp', '<(angle_path)/src/common/utilities_unittest.cpp', + '<(angle_path)/src/libANGLE/BinaryStream_unittest.cpp', '<(angle_path)/src/libANGLE/Config_unittest.cpp', '<(angle_path)/src/libANGLE/Fence_unittest.cpp', '<(angle_path)/src/libANGLE/HandleAllocator_unittest.cpp', @@ -46,6 +47,7 @@ '<(angle_path)/src/tests/compiler_tests/ExpressionLimit_test.cpp', '<(angle_path)/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp', '<(angle_path)/src/tests/compiler_tests/FragDepth_test.cpp', + '<(angle_path)/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp', '<(angle_path)/src/tests/compiler_tests/IntermNode_test.cpp', '<(angle_path)/src/tests/compiler_tests/MalformedShader_test.cpp', '<(angle_path)/src/tests/compiler_tests/NV_draw_buffers_test.cpp', diff --git a/gfx/angle/src/tests/compiler_tests/CollectVariables_test.cpp b/gfx/angle/src/tests/compiler_tests/CollectVariables_test.cpp index 0005790a62..f29374f544 100644 --- a/gfx/angle/src/tests/compiler_tests/CollectVariables_test.cpp +++ b/gfx/angle/src/tests/compiler_tests/CollectVariables_test.cpp @@ -7,6 +7,8 @@ // Some tests for shader inspection // +#include + #include "angle_gl.h" #include "gtest/gtest.h" #include "GLSLANG/ShaderLang.h" @@ -18,14 +20,10 @@ class CollectVariablesTest : public testing::Test { public: - CollectVariablesTest(GLenum shaderType) - : mShaderType(shaderType), - mTranslator(nullptr) - { - } + CollectVariablesTest(GLenum shaderType) : mShaderType(shaderType) {} protected: - virtual void SetUp() + void SetUp() override { ShBuiltInResources resources; ShInitBuiltInResources(&resources); @@ -34,16 +32,10 @@ class CollectVariablesTest : public testing::Test initTranslator(resources); } - virtual void TearDown() - { - SafeDelete(mTranslator); - } - void initTranslator(const ShBuiltInResources &resources) { - SafeDelete(mTranslator); - mTranslator = new TranslatorGLSL( - mShaderType, SH_GLES3_SPEC, SH_GLSL_COMPATIBILITY_OUTPUT); + mTranslator.reset( + new TranslatorGLSL(mShaderType, SH_GLES3_SPEC, SH_GLSL_COMPATIBILITY_OUTPUT)); ASSERT_TRUE(mTranslator->Init(resources)); } @@ -113,8 +105,14 @@ class CollectVariablesTest : public testing::Test *outResult = &outputVariable; } + void compile(const std::string &shaderString) + { + const char *shaderStrings[] = {shaderString.c_str()}; + ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES)); + } + GLenum mShaderType; - TranslatorGLSL *mTranslator; + std::unique_ptr mTranslator; }; class CollectVertexVariablesTest : public CollectVariablesTest @@ -139,8 +137,7 @@ TEST_F(CollectFragmentVariablesTest, SimpleOutputVar) " out_fragColor = vec4(1.0);\n" "}\n"; - const char *shaderStrings[] = { shaderString.c_str() }; - ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES)); + compile(shaderString); const auto &outputVariables = mTranslator->getOutputVariables(); ASSERT_EQ(1u, outputVariables.size()); @@ -165,8 +162,7 @@ TEST_F(CollectFragmentVariablesTest, LocationOutputVar) " out_fragColor = vec4(1.0);\n" "}\n"; - const char *shaderStrings[] = { shaderString.c_str() }; - ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES)); + compile(shaderString); const auto &outputVariables = mTranslator->getOutputVariables(); ASSERT_EQ(1u, outputVariables.size()); @@ -190,8 +186,7 @@ TEST_F(CollectVertexVariablesTest, LocationAttribute) " gl_Position = in_Position;\n" "}\n"; - const char *shaderStrings[] = { shaderString.c_str() }; - ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES)); + compile(shaderString); const std::vector &attributes = mTranslator->getAttributes(); ASSERT_EQ(1u, attributes.size()); @@ -217,8 +212,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInterfaceBlock) " gl_Position = vec4(f, 0.0, 0.0, 1.0);\n" "}\n"; - const char *shaderStrings[] = { shaderString.c_str() }; - ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES)); + compile(shaderString); const std::vector &interfaceBlocks = mTranslator->getInterfaceBlocks(); ASSERT_EQ(1u, interfaceBlocks.size()); @@ -254,8 +248,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInstancedInterfaceBlock) " gl_Position = vec4(blockInstance.f, 0.0, 0.0, 1.0);\n" "}\n"; - const char *shaderStrings[] = { shaderString.c_str() }; - ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES)); + compile(shaderString); const std::vector &interfaceBlocks = mTranslator->getInterfaceBlocks(); ASSERT_EQ(1u, interfaceBlocks.size()); @@ -266,6 +259,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInstancedInterfaceBlock) EXPECT_FALSE(interfaceBlock.isRowMajorLayout); EXPECT_EQ(sh::BLOCKLAYOUT_SHARED, interfaceBlock.layout); EXPECT_EQ("b", interfaceBlock.name); + EXPECT_EQ("blockInstance", interfaceBlock.instanceName); EXPECT_TRUE(interfaceBlock.staticUse); ASSERT_EQ(1u, interfaceBlock.fields.size()); @@ -275,7 +269,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInstancedInterfaceBlock) EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision); EXPECT_TRUE(field.staticUse); EXPECT_GLENUM_EQ(GL_FLOAT, field.type); - EXPECT_EQ("b.f", field.name); + EXPECT_EQ("f", field.name); EXPECT_FALSE(field.isRowMajorLayout); EXPECT_TRUE(field.fields.empty()); } @@ -292,8 +286,7 @@ TEST_F(CollectVertexVariablesTest, StructInterfaceBlock) " gl_Position = vec4(s.f, 0.0, 0.0, 1.0);\n" "}\n"; - const char *shaderStrings[] = { shaderString.c_str() }; - ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES)); + compile(shaderString); const std::vector &interfaceBlocks = mTranslator->getInterfaceBlocks(); ASSERT_EQ(1u, interfaceBlocks.size()); @@ -336,8 +329,7 @@ TEST_F(CollectVertexVariablesTest, StructInstancedInterfaceBlock) " gl_Position = vec4(instanceName.s.f, 0.0, 0.0, 1.0);\n" "}\n"; - const char *shaderStrings[] = { shaderString.c_str() }; - ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES)); + compile(shaderString); const std::vector &interfaceBlocks = mTranslator->getInterfaceBlocks(); ASSERT_EQ(1u, interfaceBlocks.size()); @@ -348,6 +340,7 @@ TEST_F(CollectVertexVariablesTest, StructInstancedInterfaceBlock) EXPECT_FALSE(interfaceBlock.isRowMajorLayout); EXPECT_EQ(sh::BLOCKLAYOUT_SHARED, interfaceBlock.layout); EXPECT_EQ("b", interfaceBlock.name); + EXPECT_EQ("instanceName", interfaceBlock.instanceName); EXPECT_TRUE(interfaceBlock.staticUse); ASSERT_EQ(1u, interfaceBlock.fields.size()); @@ -356,7 +349,7 @@ TEST_F(CollectVertexVariablesTest, StructInstancedInterfaceBlock) EXPECT_TRUE(field.isStruct()); EXPECT_TRUE(field.staticUse); - EXPECT_EQ("b.s", field.name); + EXPECT_EQ("s", field.name); EXPECT_FALSE(field.isRowMajorLayout); const sh::ShaderVariable &member = field.fields[0]; @@ -380,8 +373,7 @@ TEST_F(CollectVertexVariablesTest, NestedStructRowMajorInterfaceBlock) " gl_Position = vec4(s.m);\n" "}\n"; - const char *shaderStrings[] = { shaderString.c_str() }; - ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES)); + compile(shaderString); const std::vector &interfaceBlocks = mTranslator->getInterfaceBlocks(); ASSERT_EQ(1u, interfaceBlocks.size()); @@ -423,8 +415,7 @@ TEST_F(CollectVertexVariablesTest, VaryingInterpolation) " vary = 1.0;\n" "}\n"; - const char *shaderStrings[] = { shaderString.c_str() }; - ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES)); + compile(shaderString); const std::vector &varyings = mTranslator->getVaryings(); ASSERT_EQ(2u, varyings.size()); @@ -654,3 +645,96 @@ TEST_F(CollectFragmentVariablesTest, OutputVarESSL1EXTBlendFuncExtendedSecondary EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type); EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision); } + +static khronos_uint64_t SimpleTestHash(const char *str, size_t len) +{ + return static_cast(len); +} + +class CollectHashedVertexVariablesTest : public CollectVertexVariablesTest +{ + protected: + void SetUp() override + { + // Initialize the translate with a hash function + ShBuiltInResources resources; + ShInitBuiltInResources(&resources); + resources.HashFunction = SimpleTestHash; + initTranslator(resources); + } +}; + +TEST_F(CollectHashedVertexVariablesTest, InstancedInterfaceBlock) +{ + const std::string &shaderString = + "#version 300 es\n" + "uniform blockName {\n" + " float field;\n" + "} blockInstance;" + "void main() {\n" + " gl_Position = vec4(blockInstance.field, 0.0, 0.0, 1.0);\n" + "}\n"; + + compile(shaderString); + + const std::vector &interfaceBlocks = mTranslator->getInterfaceBlocks(); + ASSERT_EQ(1u, interfaceBlocks.size()); + + const sh::InterfaceBlock &interfaceBlock = interfaceBlocks[0]; + + EXPECT_EQ(0u, interfaceBlock.arraySize); + EXPECT_FALSE(interfaceBlock.isRowMajorLayout); + EXPECT_EQ(sh::BLOCKLAYOUT_SHARED, interfaceBlock.layout); + EXPECT_EQ("blockName", interfaceBlock.name); + EXPECT_EQ("blockInstance", interfaceBlock.instanceName); + EXPECT_EQ("webgl_9", interfaceBlock.mappedName); + EXPECT_TRUE(interfaceBlock.staticUse); + + ASSERT_EQ(1u, interfaceBlock.fields.size()); + + const sh::InterfaceBlockField &field = interfaceBlock.fields[0]; + + EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision); + EXPECT_TRUE(field.staticUse); + EXPECT_GLENUM_EQ(GL_FLOAT, field.type); + EXPECT_EQ("field", field.name); + EXPECT_EQ("webgl_5", field.mappedName); + EXPECT_FALSE(field.isRowMajorLayout); + EXPECT_TRUE(field.fields.empty()); +} + +TEST_F(CollectHashedVertexVariablesTest, StructUniform) +{ + const std::string &shaderString = + "#version 300 es\n" + "struct sType {\n" + " float field;\n" + "};" + "uniform sType u;" + "void main() {\n" + " gl_Position = vec4(u.field, 0.0, 0.0, 1.0);\n" + "}\n"; + + compile(shaderString); + + const auto &uniforms = mTranslator->getUniforms(); + ASSERT_EQ(1u, uniforms.size()); + + const sh::Uniform &uniform = uniforms[0]; + + EXPECT_EQ(0u, uniform.arraySize); + EXPECT_EQ("u", uniform.name); + EXPECT_EQ("webgl_1", uniform.mappedName); + EXPECT_TRUE(uniform.staticUse); + + ASSERT_EQ(1u, uniform.fields.size()); + + const sh::ShaderVariable &field = uniform.fields[0]; + + EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision); + // EXPECT_TRUE(field.staticUse); // we don't yet support struct static use + EXPECT_GLENUM_EQ(GL_FLOAT, field.type); + EXPECT_EQ("field", field.name); + EXPECT_EQ("webgl_5", field.mappedName); + EXPECT_TRUE(field.fields.empty()); +} diff --git a/gfx/angle/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp b/gfx/angle/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp index 77ae9295e0..40aeed28db 100644 --- a/gfx/angle/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp +++ b/gfx/angle/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp @@ -146,7 +146,7 @@ class EXTBlendFuncExtendedTest { DestroyCompiler(); mCompiler = ShConstructCompiler(GL_FRAGMENT_SHADER, testing::get<0>(GetParam()), - SH_GLSL_OUTPUT, &mResources); + SH_GLSL_COMPATIBILITY_OUTPUT, &mResources); ASSERT_TRUE(mCompiler != NULL) << "Compiler could not be constructed."; } diff --git a/gfx/angle/src/tests/compiler_tests/FragDepth_test.cpp b/gfx/angle/src/tests/compiler_tests/FragDepth_test.cpp index f0bc01aca5..d3c87987ac 100644 --- a/gfx/angle/src/tests/compiler_tests/FragDepth_test.cpp +++ b/gfx/angle/src/tests/compiler_tests/FragDepth_test.cpp @@ -41,8 +41,8 @@ class FragDepthTest : public testing::TestWithParam void InitializeCompiler() { DestroyCompiler(); - mCompiler = - ShConstructCompiler(GL_FRAGMENT_SHADER, SH_GLES3_SPEC, SH_GLSL_OUTPUT, &mResources); + mCompiler = ShConstructCompiler(GL_FRAGMENT_SHADER, SH_GLES3_SPEC, + SH_GLSL_COMPATIBILITY_OUTPUT, &mResources); ASSERT_TRUE(mCompiler != nullptr) << "Compiler could not be constructed."; } diff --git a/gfx/angle/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp b/gfx/angle/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp new file mode 100644 index 0000000000..e430df963b --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp @@ -0,0 +1,50 @@ +// +// Copyright (c) 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. +// +// GLSLCompatibilityOutputTest.cpp +// Test compiler output for glsl compatibility mode +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "tests/test_utils/compiler_test.h" + +class GLSLCompatibilityOutputTest : public testing::Test +{ + public: + GLSLCompatibilityOutputTest() {} + + protected: + void compile(const std::string &shaderString) + { + int compilationFlags = SH_VARIABLES; + + std::string infoLog; + bool compilationSuccess = + compileTestShader(GL_VERTEX_SHADER, SH_GLES2_SPEC, SH_GLSL_COMPATIBILITY_OUTPUT, + shaderString, compilationFlags, &mTranslatedSource, &infoLog); + if (!compilationSuccess) + { + FAIL() << "Shader compilation failed " << infoLog; + } + } + + bool find(const char *name) const { return mTranslatedSource.find(name) != std::string::npos; } + + private: + std::string mTranslatedSource; +}; + +// Verify gl_Position is written when compiling in compatibility mode +TEST_F(GLSLCompatibilityOutputTest, GLPositionWrittenTest) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void main() {\n" + "}"; + compile(shaderString); + EXPECT_TRUE(find("gl_Position")); +} \ No newline at end of file diff --git a/gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp b/gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp index 6e6d06c737..b072834785 100644 --- a/gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp +++ b/gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp @@ -15,7 +15,7 @@ class MalformedShaderTest : public testing::Test { public: - MalformedShaderTest() {} + MalformedShaderTest() : mExtraCompileOptions(0) {} protected: virtual void SetUp() @@ -36,7 +36,8 @@ class MalformedShaderTest : public testing::Test bool compile(const std::string& shaderString) { const char *shaderStrings[] = { shaderString.c_str() }; - bool compilationSuccess = mTranslator->compile(shaderStrings, 1, SH_INTERMEDIATE_TREE); + bool compilationSuccess = + mTranslator->compile(shaderStrings, 1, SH_INTERMEDIATE_TREE | mExtraCompileOptions); TInfoSink &infoSink = mTranslator->getInfoSink(); mInfoLog = infoSink.info.c_str(); return compilationSuccess; @@ -50,6 +51,7 @@ class MalformedShaderTest : public testing::Test protected: std::string mInfoLog; TranslatorESSL *mTranslator; + int mExtraCompileOptions; }; class MalformedVertexShaderTest : public MalformedShaderTest @@ -84,6 +86,28 @@ class MalformedWebGL2ShaderTest : public MalformedShaderTest } }; +class MalformedWebGL1ShaderTest : public MalformedShaderTest +{ + public: + MalformedWebGL1ShaderTest() {} + + protected: + void SetUp() override + { + ShBuiltInResources resources; + ShInitBuiltInResources(&resources); + + mTranslator = new TranslatorESSL(GL_FRAGMENT_SHADER, SH_WEBGL_SPEC); + ASSERT_TRUE(mTranslator->Init(resources)); + } +}; + +class UnrollForLoopsTest : public MalformedShaderTest +{ + public: + UnrollForLoopsTest() { mExtraCompileOptions = SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX; } +}; + // This is a test for a bug that used to exist in ANGLE: // Calling a function with all parameters missing should not succeed. TEST_F(MalformedShaderTest, FunctionParameterMismatch) @@ -1315,3 +1339,196 @@ TEST_F(MalformedShaderTest, TextureLodOffsetOutOfRange) FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; } } + +// Test that default precision qualifier for uint is not accepted. +// ESSL 3.00.4 section 4.5.4: Only allowed for float, int and sampler types. +TEST_F(MalformedShaderTest, DefaultPrecisionUint) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision mediump uint;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that sampler3D needs to be precision qualified. +// ESSL 3.00.4 section 4.5.4: New ESSL 3.00 sampler types don't have predefined precision. +TEST_F(MalformedShaderTest, NoPrecisionSampler3D) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform sampler3D s;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that using a non-constant expression in a for loop initializer is forbidden in WebGL 1.0, +// even when ANGLE is able to constant fold the initializer. +// ESSL 1.00 Appendix A. +TEST_F(MalformedWebGL1ShaderTest, NonConstantLoopIndex) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform int u;\n" + "void main()\n" + "{\n" + " for (int i = (true ? 1 : u); i < 5; ++i) {\n" + " gl_FragColor = vec4(0.0);\n" + " }\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Regression test for an old crash bug in ANGLE. +// ForLoopUnroll used to crash when it encountered a while loop. +TEST_F(UnrollForLoopsTest, WhileLoop) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void main()\n" + "{\n" + " while (true) {\n" + " gl_FragColor = vec4(0.0);\n" + " break;\n" + " }\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Regression test for an old crash bug in ANGLE. +// ForLoopUnroll used to crash when it encountered a loop that didn't fit the ESSL 1.00 +// Appendix A limitations. +TEST_F(UnrollForLoopsTest, UnlimitedForLoop) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void main()\n" + "{\n" + " for (;true;) {\n" + " gl_FragColor = vec4(0.0);\n" + " break;\n" + " }\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Check that indices that are not integers are rejected. +// The check should be done even if ESSL 1.00 Appendix A limitations are not applied. +TEST_F(MalformedShaderTest, NonIntegerIndex) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void main()\n" + "{\n" + " float f[3];\n" + " const float i = 2.0;\n" + " gl_FragColor = vec4(f[i]);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// ESSL1 shaders with a duplicate function prototype should be rejected. +// ESSL 1.00.17 section 4.2.7. +TEST_F(MalformedShaderTest, DuplicatePrototypeESSL1) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void foo();\n" + "void foo();\n" + "void foo() {}\n" + "void main()\n" + "{\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// ESSL3 shaders with a duplicate function prototype should be allowed. +// ESSL 3.00.4 section 4.2.3. +TEST_F(MalformedShaderTest, DuplicatePrototypeESSL3) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void foo();\n" + "void foo();\n" + "void foo() {}\n" + "void main()\n" + "{\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Shaders with a local function prototype should be rejected. +// ESSL 3.00.4 section 4.2.4. +TEST_F(MalformedShaderTest, LocalFunctionPrototype) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " void foo();\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// ESSL 3.00 fragment shaders can not use #pragma STDGL invariant(all). +// ESSL 3.00.4 section 4.6.1. Does not apply to other versions of ESSL. +TEST_F(MalformedShaderTest, ESSL300FragmentInvariantAll) +{ + const std::string &shaderString = + "#version 300 es\n" + "#pragma STDGL invariant(all)\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} diff --git a/gfx/angle/src/tests/compiler_tests/ShCompile_test.cpp b/gfx/angle/src/tests/compiler_tests/ShCompile_test.cpp index 5d31a55776..2a514a9e4f 100644 --- a/gfx/angle/src/tests/compiler_tests/ShCompile_test.cpp +++ b/gfx/angle/src/tests/compiler_tests/ShCompile_test.cpp @@ -20,8 +20,8 @@ class ShCompileTest : public testing::Test void SetUp() override { ShInitBuiltInResources(&mResources); - mCompiler = - ShConstructCompiler(GL_FRAGMENT_SHADER, SH_WEBGL_SPEC, SH_GLSL_OUTPUT, &mResources); + mCompiler = ShConstructCompiler(GL_FRAGMENT_SHADER, SH_WEBGL_SPEC, + SH_GLSL_COMPATIBILITY_OUTPUT, &mResources); ASSERT_TRUE(mCompiler != nullptr) << "Compiler could not be constructed."; } diff --git a/gfx/angle/src/tests/compiler_tests/ShaderExtension_test.cpp b/gfx/angle/src/tests/compiler_tests/ShaderExtension_test.cpp index b8098a46c0..a23de3dee4 100644 --- a/gfx/angle/src/tests/compiler_tests/ShaderExtension_test.cpp +++ b/gfx/angle/src/tests/compiler_tests/ShaderExtension_test.cpp @@ -73,7 +73,8 @@ class ShaderExtensionTest : public testing::Test void InitializeCompiler() { DestroyCompiler(); - mCompiler = ShConstructCompiler(GL_FRAGMENT_SHADER, SH_WEBGL_SPEC, SH_GLSL_OUTPUT, &mResources); + mCompiler = ShConstructCompiler(GL_FRAGMENT_SHADER, SH_WEBGL_SPEC, + SH_GLSL_COMPATIBILITY_OUTPUT, &mResources); ASSERT_TRUE(mCompiler != NULL) << "Compiler could not be constructed."; } diff --git a/gfx/angle/src/tests/compiler_tests/ShaderVariable_test.cpp b/gfx/angle/src/tests/compiler_tests/ShaderVariable_test.cpp index 9a5e144b40..736fe70964 100644 --- a/gfx/angle/src/tests/compiler_tests/ShaderVariable_test.cpp +++ b/gfx/angle/src/tests/compiler_tests/ShaderVariable_test.cpp @@ -230,7 +230,8 @@ TEST(ShaderVariableTest, InvariantDoubleDeleteBug) ShBuiltInResources resources; ShInitBuiltInResources(&resources); - ShHandle compiler = ShConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC, SH_GLSL_OUTPUT, &resources); + ShHandle compiler = ShConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC, + SH_GLSL_COMPATIBILITY_OUTPUT, &resources); EXPECT_NE(static_cast(0), compiler); const char *program[] = diff --git a/gfx/angle/src/tests/compiler_tests/TypeTracking_test.cpp b/gfx/angle/src/tests/compiler_tests/TypeTracking_test.cpp index 10b63a11d3..516c94a16c 100644 --- a/gfx/angle/src/tests/compiler_tests/TypeTracking_test.cpp +++ b/gfx/angle/src/tests/compiler_tests/TypeTracking_test.cpp @@ -297,7 +297,7 @@ TEST_F(TypeTrackingTest, PackResultTypeAndPrecision) const std::string &shaderString = "#version 300 es\n" "precision mediump float;\n" - "precision mediump uint;\n" + "precision mediump int;\n" "uniform vec2 uv;\n" "out vec4 my_FragColor;\n" "void main() {\n" @@ -323,7 +323,7 @@ TEST_F(TypeTrackingTest, UnpackNormResultTypeAndPrecision) const std::string &shaderString = "#version 300 es\n" "precision mediump float;\n" - "precision mediump uint;\n" + "precision mediump int;\n" "uniform uint uu;\n" "out vec4 my_FragColor;\n" "void main() {\n" @@ -347,7 +347,7 @@ TEST_F(TypeTrackingTest, UnpackHalfResultTypeAndPrecision) const std::string &shaderString = "#version 300 es\n" "precision highp float;\n" - "precision highp uint;\n" + "precision highp int;\n" "uniform uint uu;\n" "out vec4 my_FragColor;\n" "void main() {\n" diff --git a/gfx/angle/src/tests/compiler_tests/UnrollFlatten_test.cpp b/gfx/angle/src/tests/compiler_tests/UnrollFlatten_test.cpp index 5b5fd2fdc2..3819c3bea4 100644 --- a/gfx/angle/src/tests/compiler_tests/UnrollFlatten_test.cpp +++ b/gfx/angle/src/tests/compiler_tests/UnrollFlatten_test.cpp @@ -26,8 +26,9 @@ class UnrollFlattenTest : public testing::Test void compile(const std::string &shaderString) { std::string infoLog; - bool compilationSuccess = compileTestShader(GL_FRAGMENT_SHADER, SH_GLES2_SPEC, SH_HLSL11_OUTPUT, - shaderString, SH_VARIABLES, &mTranslatedSource, &infoLog); + bool compilationSuccess = + compileTestShader(GL_FRAGMENT_SHADER, SH_GLES2_SPEC, SH_HLSL_4_1_OUTPUT, shaderString, + SH_VARIABLES, &mTranslatedSource, &infoLog); if (!compilationSuccess) { FAIL() << "Shader compilation failed " << infoLog; diff --git a/gfx/angle/src/tests/deqp.gypi b/gfx/angle/src/tests/deqp.gypi index 0bd25eba4a..eee1e23bc5 100644 --- a/gfx/angle/src/tests/deqp.gypi +++ b/gfx/angle/src/tests/deqp.gypi @@ -19,7 +19,6 @@ 'angle_standalone%': '<(angle_standalone)', 'deqp_path': '<(DEPTH)/third_party/deqp/src', - 'delibs_path': '<(deqp_path)/framework/delibs', 'libpng_path': '<(DEPTH)/third_party/libpng', 'zlib_path': '<(DEPTH)/third_party/zlib', @@ -28,104 +27,32 @@ 'angle_build_deqp_executables%' : 0, 'angle_build_deqp_gtest_executables%' :0, - 'conditions': + 'clang_warning_flags': [ - ['(OS=="win" or OS=="linux" or OS=="mac")', - { - # Build the dEQP libraries for all Windows/Linux builds - 'angle_build_deqp_libraries%': 1, - }], - ['((OS=="win" or OS=="linux" or OS=="mac") and angle_build_winrt==0)', - { - # Build the dEQP GoogleTest support helpers for all Windows/Linux builds except WinRT - # GoogleTest doesn't support WinRT - 'angle_build_deqp_gtest_support%': 1, - }], - ['((OS=="win" or OS=="linux" or OS=="mac") and angle_standalone==1 and angle_build_winrt==0)', - { - # Build the dEQP executables for all standalone Windows/Linux builds except WinRT - # GYP doesn't support generating standalone WinRT executables - 'angle_build_deqp_executables%': 1, - - # Build the GoogleTest versions of dEQP for all standalone Windows/Linux builds except WinRT - # GoogleTest doesn't support WinRT - 'angle_build_deqp_gtest_executables%': 1, - }], - - ['OS=="win"', - { - 'deqp_include_dirs': - [ - '<(deqp_path)/framework/platform/win32', - ], - 'deqp_libtester_sources': - [ - '<(deqp_path)/framework/delibs/dethread/win32/deMutexWin32.c', - '<(deqp_path)/framework/delibs/dethread/win32/deSemaphoreWin32.c', - '<(deqp_path)/framework/delibs/dethread/win32/deThreadLocalWin32.c', - '<(deqp_path)/framework/delibs/dethread/win32/deThreadWin32.c', - ], - }], - ['OS=="linux" and use_x11==1', - { - 'deqp_include_dirs': - [ - '<(deqp_path)/framework/platform/x11', - ], - 'deqp_defines': - [ - # Ask the system headers to expose all the regular function otherwise - # dEQP doesn't compile and produces warnings about implicitly defined - # functions. - # This has to be GNU_SOURCE as on Linux dEQP uses syscall() - '_GNU_SOURCE', - ], - }], - ['OS=="mac"', - { - 'deqp_include_dirs': - [ - '<(deqp_path)/framework/platform/osx', - ], - 'deqp_defines': - [ - # Ask the system headers to expose all the regular function otherwise - # dEQP doesn't compile and produces warnings about implicitly defined - # functions. - '_XOPEN_SOURCE=600', - ], - }], - ['(OS=="linux" and use_x11==1) or OS=="mac"', - { - 'deqp_libtester_sources': - [ - '<(deqp_path)/framework/delibs/dethread/unix/deMutexUnix.c', - '<(deqp_path)/framework/delibs/dethread/unix/deNamedSemaphoreUnix.c', - '<(deqp_path)/framework/delibs/dethread/unix/deSemaphoreUnix.c', - '<(deqp_path)/framework/delibs/dethread/unix/deThreadLocalUnix.c', - '<(deqp_path)/framework/delibs/dethread/unix/deThreadUnix.c', - ], - }], + # tcu::CommandLine has virtual functions but no virtual destructor + '-Wno-no-delete-non-virtual-dtor', ], - 'deqp_msvs_disabled_warnings': + 'deqp_win_cflags': [ - '4091', # typedef ignored when no variable is declared - '4100', - '4127', # conditional expression constant - '4244', # possible loss of data - '4245', # argument signed/unsigned mismatch - '4297', # function assumed not to throw an exception but does - '4389', # signed/unsigned mismatch - '4510', # default constructor could not be generated - '4512', - '4610', # cannot be instantiated - '4611', # setjmp interaction non-portable - '4701', # potentially uninit used - '4702', # unreachable code - '4706', - '4838', # conversion requires a narrowing conversion - '4996', # deprecated + '/EHsc', # dEQP requires exceptions + '/wd4091', # typedef ignored when no variable is declared + '/wd4100', + '/wd4125', # decimal digit terminates octal escape sequence + '/wd4127', # conditional expression constant + '/wd4244', # possible loss of data + '/wd4245', # argument signed/unsigned mismatch + '/wd4297', # function assumed not to throw an exception but does + '/wd4389', # signed/unsigned mismatch + '/wd4510', # default constructor could not be generated + '/wd4512', + '/wd4610', # cannot be instantiated + '/wd4611', # setjmp interaction non-portable + '/wd4701', # potentially uninit used + '/wd4702', # unreachable code + '/wd4706', # assignment within conditional expression + '/wd4838', # conversion requires a narrowing conversion + '/wd4996', # deprecated ], 'deqp_defines': [ @@ -146,24 +73,24 @@ ], 'deqp_include_dirs': [ - '<(libpng_path)', - '<(zlib_path)', - '<(delibs_path)/debase', - '<(delibs_path)/decpp', - '<(delibs_path)/depool', - '<(delibs_path)/dethread', - '<(delibs_path)/deutil', - '<(delibs_path)/destream', + '<(deqp_path)/executor', + '<(deqp_path)/execserver', '<(deqp_path)/framework/common', - '<(deqp_path)/framework/qphelper', - '<(deqp_path)/framework/platform/null', + '<(deqp_path)/framework/delibs/debase', + '<(deqp_path)/framework/delibs/decpp', + '<(deqp_path)/framework/delibs/depool', + '<(deqp_path)/framework/delibs/dethread', + '<(deqp_path)/framework/delibs/deutil', + '<(deqp_path)/framework/delibs/destream', '<(deqp_path)/framework/egl', '<(deqp_path)/framework/egl/wrapper', '<(deqp_path)/framework/opengl', - '<(deqp_path)/framework/opengl/wrapper', - '<(deqp_path)/framework/referencerenderer', '<(deqp_path)/framework/opengl/simplereference', + '<(deqp_path)/framework/opengl/wrapper', + '<(deqp_path)/framework/platform/null', + '<(deqp_path)/framework/qphelper', '<(deqp_path)/framework/randomshaders', + '<(deqp_path)/framework/referencerenderer', '<(deqp_path)/modules/gles2', '<(deqp_path)/modules/gles2/functional', '<(deqp_path)/modules/gles2/accuracy', @@ -181,8 +108,8 @@ '<(deqp_path)/modules/gles31/stress', '<(deqp_path)/modules/glshared', '<(deqp_path)/modules/glusecases', - '<(deqp_path)/executor', - '<(deqp_path)/execserver', + '<(libpng_path)', + '<(zlib_path)', ], 'deqp_gles2_sources': [ @@ -684,46 +611,86 @@ '<(deqp_path)/modules/egl/teglAndroidUtil.hpp', '<(deqp_path)/modules/egl/teglApiCase.cpp', '<(deqp_path)/modules/egl/teglApiCase.hpp', + '<(deqp_path)/modules/egl/teglBufferAgeTests.cpp', + '<(deqp_path)/modules/egl/teglBufferAgeTests.hpp', '<(deqp_path)/modules/egl/teglChooseConfigReference.cpp', '<(deqp_path)/modules/egl/teglChooseConfigReference.hpp', '<(deqp_path)/modules/egl/teglChooseConfigTests.cpp', '<(deqp_path)/modules/egl/teglChooseConfigTests.hpp', - '<(deqp_path)/modules/egl/teglQueryConfigTests.cpp', - '<(deqp_path)/modules/egl/teglQueryConfigTests.hpp', + '<(deqp_path)/modules/egl/teglClientExtensionTests.cpp', + '<(deqp_path)/modules/egl/teglClientExtensionTests.hpp', '<(deqp_path)/modules/egl/teglColorClearCase.cpp', '<(deqp_path)/modules/egl/teglColorClearCase.hpp', '<(deqp_path)/modules/egl/teglColorClearTests.cpp', '<(deqp_path)/modules/egl/teglColorClearTests.hpp', '<(deqp_path)/modules/egl/teglConfigList.cpp', '<(deqp_path)/modules/egl/teglConfigList.hpp', + '<(deqp_path)/modules/egl/teglCreateContextExtTests.cpp', + '<(deqp_path)/modules/egl/teglCreateContextExtTests.hpp', '<(deqp_path)/modules/egl/teglCreateContextTests.cpp', '<(deqp_path)/modules/egl/teglCreateContextTests.hpp', - '<(deqp_path)/modules/egl/teglQueryContextTests.cpp', - '<(deqp_path)/modules/egl/teglQueryContextTests.hpp', '<(deqp_path)/modules/egl/teglCreateSurfaceTests.cpp', '<(deqp_path)/modules/egl/teglCreateSurfaceTests.hpp', - '<(deqp_path)/modules/egl/teglQuerySurfaceTests.cpp', - '<(deqp_path)/modules/egl/teglQuerySurfaceTests.hpp', '<(deqp_path)/modules/egl/teglGetProcAddressTests.cpp', '<(deqp_path)/modules/egl/teglGetProcAddressTests.hpp', '<(deqp_path)/modules/egl/teglGLES1RenderUtil.cpp', '<(deqp_path)/modules/egl/teglGLES1RenderUtil.hpp', '<(deqp_path)/modules/egl/teglGLES2RenderUtil.cpp', '<(deqp_path)/modules/egl/teglGLES2RenderUtil.hpp', - '<(deqp_path)/modules/egl/teglImageUtil.cpp', - '<(deqp_path)/modules/egl/teglImageUtil.hpp', + '<(deqp_path)/modules/egl/teglGLES2SharedRenderingPerfTests.cpp', + '<(deqp_path)/modules/egl/teglGLES2SharedRenderingPerfTests.hpp', + '<(deqp_path)/modules/egl/teglGLES2SharingTests.cpp', + '<(deqp_path)/modules/egl/teglGLES2SharingTests.hpp', + '<(deqp_path)/modules/egl/teglGLES2SharingThreadedTests.cpp', + '<(deqp_path)/modules/egl/teglGLES2SharingThreadedTests.hpp', + '<(deqp_path)/modules/egl/teglImageFormatTests.cpp', + '<(deqp_path)/modules/egl/teglImageFormatTests.hpp', '<(deqp_path)/modules/egl/teglImageTests.cpp', '<(deqp_path)/modules/egl/teglImageTests.hpp', + '<(deqp_path)/modules/egl/teglImageUtil.cpp', + '<(deqp_path)/modules/egl/teglImageUtil.hpp', '<(deqp_path)/modules/egl/teglInfoTests.cpp', '<(deqp_path)/modules/egl/teglInfoTests.hpp', + '<(deqp_path)/modules/egl/teglMakeCurrentPerfTests.cpp', + '<(deqp_path)/modules/egl/teglMakeCurrentPerfTests.hpp', + '<(deqp_path)/modules/egl/teglMemoryStressTests.cpp', + '<(deqp_path)/modules/egl/teglMemoryStressTests.hpp', + '<(deqp_path)/modules/egl/teglMultiThreadTests.cpp', + '<(deqp_path)/modules/egl/teglMultiThreadTests.hpp', + '<(deqp_path)/modules/egl/teglNativeColorMappingTests.cpp', + '<(deqp_path)/modules/egl/teglNativeColorMappingTests.hpp', + '<(deqp_path)/modules/egl/teglNativeCoordMappingTests.cpp', + '<(deqp_path)/modules/egl/teglNativeCoordMappingTests.hpp', '<(deqp_path)/modules/egl/teglNegativeApiTests.cpp', '<(deqp_path)/modules/egl/teglNegativeApiTests.hpp', + '<(deqp_path)/modules/egl/teglNegativePartialUpdateTests.cpp', + '<(deqp_path)/modules/egl/teglNegativePartialUpdateTests.hpp', + '<(deqp_path)/modules/egl/teglPreservingSwapTests.cpp', + '<(deqp_path)/modules/egl/teglPreservingSwapTests.hpp', + '<(deqp_path)/modules/egl/teglPartialUpdateTests.cpp', + '<(deqp_path)/modules/egl/teglPartialUpdateTests.hpp', + '<(deqp_path)/modules/egl/teglQueryConfigTests.cpp', + '<(deqp_path)/modules/egl/teglQueryConfigTests.hpp', + '<(deqp_path)/modules/egl/teglQueryContextTests.cpp', + '<(deqp_path)/modules/egl/teglQueryContextTests.hpp', + '<(deqp_path)/modules/egl/teglQuerySurfaceTests.cpp', + '<(deqp_path)/modules/egl/teglQuerySurfaceTests.hpp', '<(deqp_path)/modules/egl/teglRenderCase.cpp', '<(deqp_path)/modules/egl/teglRenderCase.hpp', '<(deqp_path)/modules/egl/teglRenderTests.cpp', '<(deqp_path)/modules/egl/teglRenderTests.hpp', + '<(deqp_path)/modules/egl/teglResizeTests.cpp', + '<(deqp_path)/modules/egl/teglResizeTests.hpp', '<(deqp_path)/modules/egl/teglSimpleConfigCase.cpp', '<(deqp_path)/modules/egl/teglSimpleConfigCase.hpp', + '<(deqp_path)/modules/egl/teglSurfacelessContextTests.cpp', + '<(deqp_path)/modules/egl/teglSurfacelessContextTests.hpp', + '<(deqp_path)/modules/egl/teglSwapBuffersTests.cpp', + '<(deqp_path)/modules/egl/teglSwapBuffersTests.hpp', + '<(deqp_path)/modules/egl/teglSwapBuffersWithDamageTests.cpp', + '<(deqp_path)/modules/egl/teglSwapBuffersWithDamageTests.hpp', + '<(deqp_path)/modules/egl/teglSyncTests.cpp', + '<(deqp_path)/modules/egl/teglSyncTests.hpp', '<(deqp_path)/modules/egl/teglTestCase.cpp', '<(deqp_path)/modules/egl/teglTestCase.hpp', '<(deqp_path)/modules/egl/teglTestPackage.cpp', @@ -731,38 +698,6 @@ '<(deqp_path)/modules/egl/teglTestPackageEntry.cpp', '<(deqp_path)/modules/egl/teglVGRenderUtil.cpp', '<(deqp_path)/modules/egl/teglVGRenderUtil.hpp', - '<(deqp_path)/modules/egl/teglImageFormatTests.hpp', - '<(deqp_path)/modules/egl/teglImageFormatTests.cpp', - '<(deqp_path)/modules/egl/teglGLES2SharingTests.hpp', - '<(deqp_path)/modules/egl/teglGLES2SharingTests.cpp', - '<(deqp_path)/modules/egl/teglGLES2SharingThreadedTests.hpp', - '<(deqp_path)/modules/egl/teglGLES2SharingThreadedTests.cpp', - '<(deqp_path)/modules/egl/teglSyncTests.hpp', - '<(deqp_path)/modules/egl/teglSyncTests.cpp', - '<(deqp_path)/modules/egl/teglMultiThreadTests.hpp', - '<(deqp_path)/modules/egl/teglMultiThreadTests.cpp', - '<(deqp_path)/modules/egl/teglMemoryStressTests.hpp', - '<(deqp_path)/modules/egl/teglMemoryStressTests.cpp', - '<(deqp_path)/modules/egl/teglMakeCurrentPerfTests.hpp', - '<(deqp_path)/modules/egl/teglMakeCurrentPerfTests.cpp', - '<(deqp_path)/modules/egl/teglGLES2SharedRenderingPerfTests.hpp', - '<(deqp_path)/modules/egl/teglGLES2SharedRenderingPerfTests.cpp', - '<(deqp_path)/modules/egl/teglPreservingSwapTests.hpp', - '<(deqp_path)/modules/egl/teglPreservingSwapTests.cpp', - '<(deqp_path)/modules/egl/teglClientExtensionTests.hpp', - '<(deqp_path)/modules/egl/teglClientExtensionTests.cpp', - '<(deqp_path)/modules/egl/teglCreateContextExtTests.hpp', - '<(deqp_path)/modules/egl/teglCreateContextExtTests.cpp', - '<(deqp_path)/modules/egl/teglSurfacelessContextTests.hpp', - '<(deqp_path)/modules/egl/teglSurfacelessContextTests.cpp', - '<(deqp_path)/modules/egl/teglSwapBuffersTests.hpp', - '<(deqp_path)/modules/egl/teglSwapBuffersTests.cpp', - '<(deqp_path)/modules/egl/teglNativeColorMappingTests.hpp', - '<(deqp_path)/modules/egl/teglNativeColorMappingTests.cpp', - '<(deqp_path)/modules/egl/teglNativeCoordMappingTests.hpp', - '<(deqp_path)/modules/egl/teglNativeCoordMappingTests.cpp', - '<(deqp_path)/modules/egl/teglResizeTests.hpp', - '<(deqp_path)/modules/egl/teglResizeTests.cpp', ], 'deqp_libtester_decpp_sources': [ @@ -782,6 +717,7 @@ '<(deqp_path)/framework/delibs/decpp/deRingBuffer.cpp', '<(deqp_path)/framework/delibs/decpp/deSemaphore.cpp', '<(deqp_path)/framework/delibs/decpp/deSharedPtr.cpp', + '<(deqp_path)/framework/delibs/decpp/deSha1.cpp', '<(deqp_path)/framework/delibs/decpp/deSocket.cpp', '<(deqp_path)/framework/delibs/decpp/deSTLUtil.cpp', '<(deqp_path)/framework/delibs/decpp/deStringUtil.cpp', @@ -860,6 +796,7 @@ '<(deqp_path)/framework/delibs/debase/deMemory.c', '<(deqp_path)/framework/delibs/debase/deRandom.c', '<(deqp_path)/framework/delibs/debase/deString.c', + '<(deqp_path)/framework/delibs/debase/deSha1.c', '<(deqp_path)/framework/delibs/deimage/deImage.c', '<(deqp_path)/framework/delibs/deimage/deTarga.c', '<(deqp_path)/framework/delibs/depool/deMemPool.c', @@ -893,7 +830,6 @@ '<(deqp_path)/framework/egl/egluDefs.cpp', '<(deqp_path)/framework/egl/egluGLContextFactory.cpp', '<(deqp_path)/framework/egl/egluGLFunctionLoader.cpp', - '<(deqp_path)/framework/egl/egluGLFunctionLoader.cpp', '<(deqp_path)/framework/egl/egluGLUtil.cpp', '<(deqp_path)/framework/egl/egluNativeDisplay.cpp', '<(deqp_path)/framework/egl/egluNativePixmap.cpp', @@ -920,6 +856,7 @@ '<(deqp_path)/framework/opengl/gluProgramInterfaceQuery.cpp', '<(deqp_path)/framework/opengl/gluRenderConfig.cpp', '<(deqp_path)/framework/opengl/gluRenderContext.cpp', + '<(deqp_path)/framework/opengl/gluShaderLibrary.cpp', '<(deqp_path)/framework/opengl/gluShaderProgram.cpp', '<(deqp_path)/framework/opengl/gluShaderUtil.cpp', '<(deqp_path)/framework/opengl/gluStateReset.cpp', @@ -1023,6 +960,83 @@ '<(angle_path)/src/tests/deqp_support/tcuRandomOrderExecutor.cpp', '<(angle_path)/src/tests/deqp_support/tcuRandomOrderExecutor.h', ], + 'deqp_libtester_sources_win': + [ + '<(deqp_path)/framework/delibs/dethread/win32/deMutexWin32.c', + '<(deqp_path)/framework/delibs/dethread/win32/deSemaphoreWin32.c', + '<(deqp_path)/framework/delibs/dethread/win32/deThreadLocalWin32.c', + '<(deqp_path)/framework/delibs/dethread/win32/deThreadWin32.c', + ], + 'deqp_libtester_sources_unix': + [ + '<(deqp_path)/framework/delibs/dethread/unix/deMutexUnix.c', + '<(deqp_path)/framework/delibs/dethread/unix/deNamedSemaphoreUnix.c', + '<(deqp_path)/framework/delibs/dethread/unix/deSemaphoreUnix.c', + '<(deqp_path)/framework/delibs/dethread/unix/deThreadLocalUnix.c', + '<(deqp_path)/framework/delibs/dethread/unix/deThreadUnix.c', + ], + 'deqp_gpu_test_expectations_sources': + [ + 'third_party/gpu_test_expectations/gpu_info.cc', + 'third_party/gpu_test_expectations/gpu_info.h', + 'third_party/gpu_test_expectations/gpu_test_config.cc', + 'third_party/gpu_test_expectations/gpu_test_config.h', + 'third_party/gpu_test_expectations/gpu_test_expectations_parser.cc', + 'third_party/gpu_test_expectations/gpu_test_expectations_parser.h', + ], + 'conditions': + [ + ['(OS=="win" or OS=="linux" or OS=="mac")', + { + # Build the dEQP libraries for all Windows/Linux builds + 'angle_build_deqp_libraries%': 1, + }], + ['((OS=="win" or OS=="linux" or OS=="mac") and angle_build_winrt==0)', + { + # Build the dEQP GoogleTest support helpers for all Windows/Linux builds except WinRT + # GoogleTest doesn't support WinRT + 'angle_build_deqp_gtest_support%': 1, + }], + ['((OS=="win" or OS=="linux" or OS=="mac") and angle_standalone==1 and angle_build_winrt==0)', + { + # Build the dEQP executables for all standalone Windows/Linux builds except WinRT + # GYP doesn't support generating standalone WinRT executables + 'angle_build_deqp_executables%': 1, + + # Build the GoogleTest versions of dEQP for all standalone Windows/Linux builds except WinRT + # GoogleTest doesn't support WinRT + 'angle_build_deqp_gtest_executables%': 1, + }], + ['OS=="linux" and use_x11==1', + { + 'deqp_include_dirs': + [ + '<(deqp_path)/framework/platform/x11', + ], + 'deqp_defines': + [ + # Ask the system headers to expose all the regular function otherwise + # dEQP doesn't compile and produces warnings about implicitly defined + # functions. + # This has to be GNU_SOURCE as on Linux dEQP uses syscall() + '_GNU_SOURCE', + ], + }], + ['OS=="mac"', + { + 'deqp_include_dirs': + [ + '<(deqp_path)/framework/platform/osx', + ], + 'deqp_defines': + [ + # Ask the system headers to expose all the regular function otherwise + # dEQP doesn't compile and produces warnings about implicitly defined + # functions. + '_XOPEN_SOURCE=600', + ], + }], + ], }, 'conditions': [ @@ -1170,12 +1184,6 @@ }, 'msvs_settings': { - 'VCCLCompilerTool': - { - # dEQP requires exceptions and RTTI - 'ExceptionHandling': 1, - 'RuntimeTypeInfo': 'true', - }, 'VCLinkerTool': { 'conditions': @@ -1204,6 +1212,28 @@ }, }, }, + + 'Debug_Base': + { + 'msvs_settings': + { + 'VCCLCompilerTool': + { + 'RuntimeTypeInfo': 'true', # dEQP needs RTTI + }, + }, + }, + + 'Release_Base': + { + 'msvs_settings': + { + 'VCCLCompilerTool': + { + 'RuntimeTypeInfo': 'true', # dEQP needs RTTI + }, + }, + }, }, # Re-enable RTTI and exceptions, dEQP needs them. 'cflags_cc!': @@ -1211,13 +1241,36 @@ '-fno-exceptions', '-fno-rtti', ], - 'msvs_disabled_warnings': - [ - '<@(deqp_msvs_disabled_warnings)', - ], 'include_dirs': ['<@(deqp_include_dirs)'], 'defines': ['<@(deqp_defines)'], 'defines!': [ '<@(deqp_undefines)' ], + 'msvs_settings': + { + 'VCCLCompilerTool': + { + 'AdditionalOptions': ['<@(deqp_win_cflags)'], + }, + }, + 'conditions': + [ + ['clang==1', + { + # TODO(jmadill): Remove this once we fix dEQP. + 'cflags_cc': + [ + '-Wno-delete-non-virtual-dtor', + ], + }], + ['OS=="win"', + { + 'cflags': ['<@(deqp_win_cflags)'], + 'cflags_cc': ['<@(deqp_win_cflags)'], + 'include_dirs': + [ + '<(deqp_path)/framework/platform/win32', + ], + }], + ], }, 'conditions': [ @@ -1316,6 +1369,14 @@ }, }, }], + ['OS=="win"', + { + 'sources': [ '<@(deqp_libtester_sources_win)', ], + }], + ['(OS=="linux" and use_x11==1) or OS=="mac"', + { + 'sources': [ '<@(deqp_libtester_sources_unix)', ], + }], ], }, @@ -1464,22 +1525,6 @@ '<(angle_path)/util/util.gyp:angle_util', ], - 'copies': - [ - { - 'destination': '<(PRODUCT_DIR)/deqp_support', - 'files': - [ - 'deqp_support/dEQP-EGL-cases.txt.gz', - 'deqp_support/dEQP-GLES2-cases.txt.gz', - 'deqp_support/dEQP-GLES3-cases.txt.gz', - 'deqp_support/deqp_egl_test_expectations.txt', - 'deqp_support/deqp_gles2_test_expectations.txt', - 'deqp_support/deqp_gles3_test_expectations.txt', - ], - }, - ], - 'direct_dependent_settings': { 'include_dirs': @@ -1489,13 +1534,8 @@ ], 'sources': [ + '<@(deqp_gpu_test_expectations_sources)', 'deqp_support/angle_deqp_gtest.cpp', - 'third_party/gpu_test_expectations/gpu_info.cc', - 'third_party/gpu_test_expectations/gpu_info.h', - 'third_party/gpu_test_expectations/gpu_test_config.cc', - 'third_party/gpu_test_expectations/gpu_test_config.h', - 'third_party/gpu_test_expectations/gpu_test_expectations_parser.cc', - 'third_party/gpu_test_expectations/gpu_test_expectations_parser.h', ], 'defines': @@ -1517,15 +1557,17 @@ 'conditions': [ - ['OS=="linux"', + # NOTE(smcgruer): Guarding with use_libpci allows gyp to run successfully + # on systems without libpci, but the test targets will not compile or link. + ['OS=="linux" and use_libpci==1', { 'ldflags': [ - ' #include -#include #include #include "angle_deqp_libtester.h" #include "common/angleutils.h" #include "common/debug.h" +#include "common/Optional.h" +#include "common/string_utils.h" #include "gpu_test_expectations_parser.h" #include "system_utils.h" namespace { -const char *g_CaseListFiles[] = -{ - "dEQP-GLES2-cases.txt.gz", - "dEQP-GLES3-cases.txt.gz", - "dEQP-EGL-cases.txt.gz", +const char *g_CaseListRelativePath = "/../../third_party/deqp/src/android/cts/master/"; + +const char *g_TestExpectationsSearchPaths[] = { + "/../../src/tests/deqp_support/", "/../../third_party/angle/src/tests/deqp_support/", + "/deqp_support/", +}; + +const char *g_CaseListFiles[] = { + "gles2-master.txt", "gles3-master.txt", "egl-master.txt", }; const char *g_TestExpectationsFiles[] = @@ -35,6 +41,33 @@ const char *g_TestExpectationsFiles[] = "deqp_egl_test_expectations.txt", }; +// During the CaseList initialization we cannot use the GTEST FAIL macro to quit the program because +// the initialization is called outside of tests the first time. +void Die() +{ + exit(EXIT_FAILURE); +} + +Optional FindTestExpectationsPath(const std::string &exeDir, + size_t testModuleIndex) +{ + for (const char *testPath : g_TestExpectationsSearchPaths) + { + std::stringstream testExpectationsPathStr; + testExpectationsPathStr << exeDir << testPath << g_TestExpectationsFiles[testModuleIndex]; + + std::string path = testExpectationsPathStr.str(); + std::ifstream inFile(path.c_str()); + if (!inFile.fail()) + { + inFile.close(); + return Optional(path); + } + } + + return Optional::Invalid(); +} + class dEQPCaseList { public: @@ -97,15 +130,17 @@ void dEQPCaseList::initialize() std::string exeDir = angle::GetExecutableDirectory(); std::stringstream caseListPathStr; - caseListPathStr << exeDir << "/deqp_support/" << g_CaseListFiles[mTestModuleIndex]; + caseListPathStr << exeDir << g_CaseListRelativePath << g_CaseListFiles[mTestModuleIndex]; std::string caseListPath = caseListPathStr.str(); - std::stringstream testExpectationsPathStr; - testExpectationsPathStr << exeDir << "/deqp_support/" - << g_TestExpectationsFiles[mTestModuleIndex]; - std::string testExpectationsPath = testExpectationsPathStr.str(); + Optional testExpectationsPath = FindTestExpectationsPath(exeDir, mTestModuleIndex); + if (!testExpectationsPath.valid()) + { + std::cerr << "Failed to find test expectations file." << std::endl; + Die(); + } - if (!mTestExpectationsParser.LoadTestExpectationsFromFile(testExpectationsPath)) + if (!mTestExpectationsParser.LoadTestExpectationsFromFile(testExpectationsPath.value())) { std::stringstream errorMsgStream; for (const auto &message : mTestExpectationsParser.GetErrorMessages()) @@ -113,52 +148,43 @@ void dEQPCaseList::initialize() errorMsgStream << std::endl << " " << message; } - FAIL() << "Failed to load test expectations." << errorMsgStream.str(); + std::cerr << "Failed to load test expectations." << errorMsgStream.str() << std::endl; + Die(); } if (!mTestConfig.LoadCurrentConfig(nullptr)) { - FAIL() << "Failed to load test configuration."; + std::cerr << "Failed to load test configuration." << std::endl; + Die(); } - std::stringstream caseListStream; - - std::vector buf(1024 * 1024 * 16); - gzFile *fi = static_cast(gzopen(caseListPath.c_str(), "rb")); - - if (fi == nullptr) + std::ifstream caseListStream(caseListPath); + if (caseListStream.fail()) { - FAIL() << "Failed to read gzipped test information."; + std::cerr << "Failed to load the case list." << std::endl; + Die(); } - gzrewind(fi); - while (!gzeof(fi)) - { - int len = gzread(fi, &buf[0], static_cast(buf.size()) - 1); - buf[len] = '\0'; - caseListStream << &buf[0]; - } - gzclose(fi); - while (!caseListStream.eof()) { std::string inString; std::getline(caseListStream, inString); - if (inString.substr(0, 4) == "TEST") + std::string dEQPName = angle::TrimString(inString, angle::kWhitespaceASCII); + if (dEQPName.empty()) + continue; + std::string gTestName = dEQPName.substr(dEQPName.find('.') + 1); + if (gTestName.empty()) + continue; + std::replace(gTestName.begin(), gTestName.end(), '.', '_'); + + // Occurs in some luminance tests + gTestName.erase(std::remove(gTestName.begin(), gTestName.end(), '-'), gTestName.end()); + + int expectation = mTestExpectationsParser.GetTestExpectation(dEQPName, mTestConfig); + if (expectation != gpu::GPUTestExpectationsParser::kGpuTestSkip) { - std::string dEQPName = inString.substr(6); - std::string gTestName = dEQPName.substr(dEQPName.find('.') + 1); - std::replace(gTestName.begin(), gTestName.end(), '.', '_'); - - // Occurs in some luminance tests - gTestName.erase(std::remove(gTestName.begin(), gTestName.end(), '-'), gTestName.end()); - - int expectation = mTestExpectationsParser.GetTestExpectation(dEQPName, mTestConfig); - if (expectation != gpu::GPUTestExpectationsParser::kGpuTestSkip) - { - mCaseInfoList.push_back(CaseInfo(dEQPName, gTestName, expectation)); - } + mCaseInfoList.push_back(CaseInfo(dEQPName, gTestName, expectation)); } } } diff --git a/gfx/angle/src/tests/deqp_support/dEQP-EGL-cases.txt.gz b/gfx/angle/src/tests/deqp_support/dEQP-EGL-cases.txt.gz deleted file mode 100644 index 56a937c014ecbf9fceb70e447260bf8617c8c869..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14560 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 X+IE*CpzBlcCb+v+IPcv72lf8|7!cHH diff --git a/gfx/angle/src/tests/deqp_support/dEQP-GLES2-cases.txt.gz b/gfx/angle/src/tests/deqp_support/dEQP-GLES2-cases.txt.gz deleted file mode 100644 index 0748b78437892efd61074f4284bc3a7dfd7d59ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79899 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�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∋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 diff --git a/gfx/angle/src/tests/deqp_support/deqp_gles2_test_expectations.txt b/gfx/angle/src/tests/deqp_support/deqp_gles2_test_expectations.txt index 5a4c823cd3..d59cc933dc 100644 --- a/gfx/angle/src/tests/deqp_support/deqp_gles2_test_expectations.txt +++ b/gfx/angle/src/tests/deqp_support/deqp_gles2_test_expectations.txt @@ -23,9 +23,32 @@ // 91531 MAC WIN LINUX : conformance_more_* = SKIP // 91532 MAC NVIDIA 0x0640 : tex_image_and_sub_image_2d_with_video = PASS FAIL -// TODO(jmadill): triage these into temporary and permanent suppressions +// TODO(jmadill): Figure out why these fail on the bots, but not locally. +1018 WIN : dEQP-GLES2.functional.shaders.invariance.highp.loop_2 = FAIL +1018 WIN : dEQP-GLES2.functional.shaders.invariance.mediump.loop_2 = FAIL + +// Skip these tests due to timeouts +1034 WIN LINUX MAC : dEQP-GLES2.functional.flush_finish.* = SKIP + +// Don't run these tests for faster turnover +998 WIN LINUX MAC : dEQP-GLES2.performance.* = SKIP +998 WIN LINUX MAC : dEQP-GLES2.stress.* = SKIP + +//////////////////////////////////////////////////////////////////////////////// +// +// Temporary entries: they should be removed once the bugs are fixed. +// +//////////////////////////////////////////////////////////////////////////////// // Windows only failures + +// These tests crash for unknown reason under a newer D3DCompiler_47.dll +// TODO(jmadill): Figure out if we can solve these crashes +1252 WIN : dEQP-GLES2.functional.shaders.random.trigonometric.vertex.63 = SKIP +1252 WIN : dEQP-GLES2.functional.shaders.random.trigonometric.fragment.63 = SKIP + +// TODO(jmadill): triage these into temporary and permanent suppressions + 1030 WIN : dEQP-GLES2.functional.attribute_location.bind_hole.mat2 = FAIL 1030 WIN : dEQP-GLES2.functional.attribute_location.bind_relink_hole.mat2 = FAIL 1027 WIN : dEQP-GLES2.functional.fbo.render.color_clear.rbo_rgb5_a1 = FAIL @@ -75,8 +98,6 @@ 1033 WIN : dEQP-GLES2.functional.polygon_offset.fixed16_render_with_units = FAIL 1015 WIN : dEQP-GLES2.functional.shaders.functions.misc.missing_returns_vertex = FAIL 1015 WIN : dEQP-GLES2.functional.shaders.functions.misc.missing_returns_fragment = FAIL -1018 WIN : dEQP-GLES2.functional.shaders.invariance.highp.loop_2 = FAIL -1018 WIN : dEQP-GLES2.functional.shaders.invariance.mediump.loop_2 = FAIL 1017 WIN : dEQP-GLES2.functional.shaders.loops.for_constant_iterations.nested_sequence_* = FAIL 1017 WIN : dEQP-GLES2.functional.shaders.loops.for_constant_iterations.nested_tricky_dataflow_* = FAIL 1017 WIN : dEQP-GLES2.functional.shaders.loops.while_constant_iterations.nested_tricky_dataflow_* = FAIL @@ -184,8 +205,6 @@ 504 LINUX : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.array_in_struct.sampler2D_samplerCube_both = FAIL 504 LINUX : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.nested_structs_arrays.sampler2D_samplerCube_both = FAIL 504 LINUX : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.nested_structs_arrays.sampler2D_samplerCube_fragment = FAIL -1034 LINUX : dEQP-GLES2.functional.flush_finish.flush = FAIL -1034 LINUX : dEQP-GLES2.functional.flush_finish.finish = FAIL 1019 LINUX : dEQP-GLES2.functional.texture.size.cube.256x256_rgba4444 = FAIL 1019 LINUX : dEQP-GLES2.functional.texture.size.cube.512x512_rgba4444 = FAIL 1020 LINUX : dEQP-GLES2.functional.texture.mipmap.2d.projected.nearest_linear* = FAIL @@ -225,12 +244,8 @@ 1143 MAC : dEQP-GLES2.functional.texture.size.cube.512x512_rgba4444 = FAIL 1143 MAC : dEQP-GLES2.functional.texture.mipmap.cube.projected.linear_linear = FAIL 1143 MAC : dEQP-GLES2.functional.texture.specification.basic_copyteximage2d.cube_* = FAIL -1143 MAC : dEQP-GLES2.functional.flush_finish.flush = FAIL -1143 MAC : dEQP-GLES2.functional.flush_finish.finish = FAIL // Windows and Linux failures -1015 WIN LINUX : dEQP-GLES2.functional.shaders.functions.invalid.local_function_prototype_vertex = FAIL -1015 WIN LINUX : dEQP-GLES2.functional.shaders.functions.invalid.local_function_prototype_fragment = FAIL 1020 WIN LINUX : dEQP-GLES2.functional.texture.mipmap.cube.projected.linear_linear = FAIL 504 WIN LINUX : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.render.nested_structs_arrays.sampler2D_samplerCube_* = FAIL @@ -239,8 +254,6 @@ 989 WIN LINUX MAC : dEQP-GLES2.functional.shaders.preprocessor.extensions.after_non_preprocessing_tokens_fragment = FAIL 1015 WIN LINUX MAC : dEQP-GLES2.functional.shaders.functions.invalid.return_array_in_struct_vertex = FAIL 1015 WIN LINUX MAC : dEQP-GLES2.functional.shaders.functions.invalid.return_array_in_struct_fragment = FAIL -996 WIN LINUX MAC : dEQP-GLES2.functional.shaders.functions.invalid.double_declare_vertex = FAIL -996 WIN LINUX MAC : dEQP-GLES2.functional.shaders.functions.invalid.double_declare_fragment = FAIL 1016 WIN LINUX MAC : dEQP-GLES2.functional.shaders.scoping.valid.local_variable_hides_function_parameter_vertex = FAIL 1016 WIN LINUX MAC : dEQP-GLES2.functional.shaders.scoping.valid.local_variable_hides_function_parameter_fragment = FAIL 1016 WIN LINUX MAC : dEQP-GLES2.functional.shaders.scoping.invalid.redeclare_function_vertex = FAIL @@ -251,11 +264,6 @@ 1025 WIN LINUX MAC : dEQP-GLES2.functional.fragment_ops.interaction.basic_shader.* = FAIL 1027 WIN LINUX MAC : dEQP-GLES2.functional.fbo.render.repeated_clear.tex2d_rgb = FAIL 1027 WIN LINUX MAC : dEQP-GLES2.functional.fbo.render.repeated_clear.tex2d_rgba = FAIL -1028 WIN LINUX MAC : dEQP-GLES2.functional.fbo.completeness.renderable.renderbuffer.color0.r16f = FAIL -1028 WIN LINUX MAC : dEQP-GLES2.functional.fbo.completeness.renderable.renderbuffer.color0.rg16f = FAIL -1028 WIN LINUX MAC : dEQP-GLES2.functional.fbo.completeness.renderable.renderbuffer.color0.rgba16f = FAIL -1028 WIN LINUX MAC : dEQP-GLES2.functional.fbo.completeness.renderable.renderbuffer.color0.rgb16f = FAIL -1028 WIN LINUX MAC : dEQP-GLES2.functional.fbo.completeness.renderable.renderbuffer.depth.depth_component32 = FAIL 1028 WIN LINUX MAC : dEQP-GLES2.functional.fbo.completeness.renderable.texture.color0.rgb_float = FAIL 1028 WIN LINUX MAC : dEQP-GLES2.functional.fbo.completeness.renderable.texture.color0.rgb_half_float_oes = FAIL 1028 WIN LINUX MAC : dEQP-GLES2.functional.fbo.completeness.renderable.texture.color0.rgba_float = FAIL @@ -288,16 +296,3 @@ 1030 WIN LINUX MAC : dEQP-GLES2.functional.attribute_location.bind_aliasing.cond_mat4_offset_1 = FAIL 1030 WIN LINUX MAC : dEQP-GLES2.functional.attribute_location.bind_aliasing.max_cond_mat4 = FAIL 1032 WIN LINUX MAC : dEQP-GLES2.functional.state_query.shader.program_attached_shaders = FAIL - -// Skip this test due to timeouts -1034 WIN LINUX MAC : dEQP-GLES2.functional.flush_finish.finish_wait = SKIP - -// Don't run these tests for faster turnover -998 WIN LINUX MAC : dEQP-GLES2.performance.* = SKIP -998 WIN LINUX MAC : dEQP-GLES2.stress.* = SKIP - -//////////////////////////////////////////////////////////////////////////////// -// -// Temprory entries: they should be removed once the bugs are fixed. -// -//////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/angle/src/tests/deqp_support/deqp_gles3_test_expectations.txt b/gfx/angle/src/tests/deqp_support/deqp_gles3_test_expectations.txt index e8f98b2e97..a8c9b3a183 100644 --- a/gfx/angle/src/tests/deqp_support/deqp_gles3_test_expectations.txt +++ b/gfx/angle/src/tests/deqp_support/deqp_gles3_test_expectations.txt @@ -26,22 +26,27 @@ // Don't run these tests for faster turnover 998 WIN LINUX : dEQP-GLES3.performance.* = SKIP 998 WIN LINUX : dEQP-GLES3.stress.* = SKIP +1101 WIN : dEQP-GLES3.functional.flush_finish.* = SKIP -// TODO(jmadill): Figure out why this fails on the bots, but not locally. +// TODO(jmadill): Figure out why these fail on the bots, but not locally. 1108 WIN : dEQP-GLES3.functional.shaders.struct.local.dynamic_loop_struct_array_fragment = FAIL +1094 WIN : dEQP-GLES3.functional.shaders.invariance.highp.loop_2 = FAIL +1094 WIN : dEQP-GLES3.functional.shaders.invariance.mediump.loop_2 = FAIL + +// We can't support distinct texture sizes in D3D11. +1097 WIN : dEQP-GLES3.functional.fbo.completeness.size.distinct = FAIL //////////////////////////////////////////////////////////////////////////////// // -// Temprory entries: they should be removed once the bugs are fixed. +// Temporary entries: they should be removed once the bugs are fixed. // //////////////////////////////////////////////////////////////////////////////// +// TODO(jmadill): Find why this fails when run in a certain sequence, but not singly. +1098 WIN : dEQP-GLES3.functional.uniform_api.random.50 = FAIL + // TODO(jmadill): triage these into permanent and temporary failures -1089 WIN : dEQP-GLES3.functional.shaders.functions.invalid.local_function_prototype_vertex = FAIL -1089 WIN : dEQP-GLES3.functional.shaders.functions.invalid.local_function_prototype_fragment = FAIL -1089 WIN : dEQP-GLES3.functional.shaders.functions.invalid.overload_builtin_function_vertex = FAIL -1089 WIN : dEQP-GLES3.functional.shaders.functions.invalid.overload_builtin_function_fragment = FAIL 1091 WIN : dEQP-GLES3.functional.shaders.loops.for_constant_iterations.nested_sequence_vertex = FAIL 1091 WIN : dEQP-GLES3.functional.shaders.loops.for_constant_iterations.nested_sequence_fragment = FAIL 1091 WIN : dEQP-GLES3.functional.shaders.loops.for_constant_iterations.nested_tricky_dataflow_1_vertex = FAIL @@ -58,7 +63,6 @@ 1091 WIN : dEQP-GLES3.functional.shaders.loops.do_while_constant_iterations.nested_tricky_dataflow_2_fragment = FAIL 504 WIN : dEQP-GLES3.functional.shaders.struct.uniform.sampler_vertex = FAIL 504 WIN : dEQP-GLES3.functional.shaders.struct.uniform.sampler_fragment = FAIL -504 WIN : dEQP-GLES3.functional.shaders.struct.uniform.sampler_nested_vertex = FAIL 504 WIN : dEQP-GLES3.functional.shaders.struct.uniform.sampler_nested_fragment = FAIL 504 WIN : dEQP-GLES3.functional.shaders.struct.uniform.sampler_array_vertex = FAIL 504 WIN : dEQP-GLES3.functional.shaders.struct.uniform.sampler_array_fragment = FAIL @@ -80,11 +84,9 @@ 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.usampler2d_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.isampler2darray_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.isampler2darray_fragment = FAIL -1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.usampler2darray_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.usampler2darray_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.isampler3d_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.isampler3d_fragment = FAIL -1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.usampler3d_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.usampler3d_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.sampler2dshadow_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.sampler2dshadow_fragment = FAIL @@ -98,7 +100,6 @@ 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojlodoffset.isampler2d_vec4_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojlodoffset.usampler2d_vec4_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojlodoffset.usampler2d_vec4_fragment = FAIL -1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojlodoffset.isampler3d_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojlodoffset.isampler3d_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojlodoffset.usampler3d_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojlodoffset.usampler3d_fragment = FAIL @@ -109,7 +110,6 @@ 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegrad.usampler2d_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegrad.usampler2d_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegrad.isamplercube_vertex = FAIL -1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegrad.isamplercube_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegrad.usamplercube_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegrad.usamplercube_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegrad.isampler2darray_vertex = FAIL @@ -122,8 +122,6 @@ 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegrad.usampler3d_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegrad.sampler2dshadow_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegrad.sampler2dshadow_fragment = FAIL -1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegrad.samplercubeshadow_vertex = FAIL -1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegrad.samplercubeshadow_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegrad.sampler2darrayshadow_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegrad.sampler2darrayshadow_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturegradoffset.isampler2d_vertex = FAIL @@ -208,30 +206,6 @@ 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturesize.usampler2darray_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturesize.sampler2darrayshadow_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturesize.sampler2darrayshadow_fragment = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.mediump_vertex.scalar = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.mediump_vertex.vec2 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.mediump_vertex.vec3 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.mediump_vertex.vec4 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.mediump_fragment.scalar = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.mediump_fragment.vec2 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.mediump_fragment.vec3 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.mediump_fragment.vec4 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.highp_vertex.scalar = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.highp_vertex.vec2 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.highp_vertex.vec3 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.highp_vertex.vec4 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.highp_fragment.scalar = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.highp_fragment.vec2 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.highp_fragment.vec3 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.asin.highp_fragment.vec4 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.atan2.highp_vertex.scalar = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.atan2.highp_vertex.vec2 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.atan2.highp_vertex.vec3 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.atan2.highp_vertex.vec4 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.atan2.highp_fragment.scalar = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.atan2.highp_fragment.vec2 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.atan2.highp_fragment.vec3 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.atan2.highp_fragment.vec4 = FAIL 1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.tanh.highp_vertex.scalar = FAIL 1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.tanh.highp_vertex.vec2 = FAIL 1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.tanh.highp_vertex.vec3 = FAIL @@ -240,10 +214,6 @@ 1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.tanh.highp_fragment.vec2 = FAIL 1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.tanh.highp_fragment.vec3 = FAIL 1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.tanh.highp_fragment.vec4 = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.modf.highp_vertex = FAIL -1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.modf.highp_fragment = FAIL -1094 WIN : dEQP-GLES3.functional.shaders.invariance.highp.loop_2 = FAIL -1094 WIN : dEQP-GLES3.functional.shaders.invariance.mediump.loop_2 = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.projected.nearest_nearest_clamp = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.projected.nearest_nearest_repeat = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.projected.nearest_nearest_mirror = FAIL @@ -260,71 +230,11 @@ 1095 WIN : dEQP-GLES3.functional.texture.mipmap.cube.base_level.linear_linear = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.3d.min_lod.nearest_nearest = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.3d.min_lod.linear_nearest = FAIL -1095 WIN : dEQP-GLES3.functional.texture.mipmap.3d.base_level.nearest_nearest = FAIL -1095 WIN : dEQP-GLES3.functional.texture.mipmap.3d.base_level.linear_nearest = FAIL -1095 WIN : dEQP-GLES3.functional.texture.swizzle.multi_channel.luminance_all_zero = FAIL -1095 WIN : dEQP-GLES3.functional.texture.swizzle.multi_channel.luminance_alpha_all_zero = FAIL -1095 WIN : dEQP-GLES3.functional.texture.swizzle.multi_channel.rg_all_zero = FAIL -1095 WIN : dEQP-GLES3.functional.texture.swizzle.multi_channel.rgb_all_zero = FAIL -1095 WIN : dEQP-GLES3.functional.texture.swizzle.multi_channel.rgba_all_zero = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rgba32i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rgba32ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rgba16i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rgba16ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rgba8i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rgba8ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rgb10_a2ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rgb32i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rgb32ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rgb16i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rgb16ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rgb8i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rgb8ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rg32i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rg32ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rg16i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rg16ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rg8i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.rg8ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.r32i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.r32ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.r16i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.r16ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.r8i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_teximage2d.r8ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.random_teximage2d.cube_0 = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.random_teximage2d.cube_3 = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.random_teximage2d.cube_7 = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.teximage2d_depth.depth_component32f = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.teximage2d_depth.depth32f_stencil8 = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.teximage2d_depth_pbo.depth_component32f = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.teximage2d_depth_pbo.depth_component24 = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.teximage2d_depth_pbo.depth32f_stencil8 = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rgba32i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rgba32ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rgba16i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rgba16ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rgba8i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rgba8ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rgb10_a2ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rgb32i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rgb32ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rgb16i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rgb16ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rgb8i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rgb8ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rg32i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rg32ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rg16i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rg16ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rg8i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.rg8ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.r32i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.r32ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.r16i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.r16ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.r8i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.basic_texsubimage2d.r8ui_cube = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.texsubimage2d_depth.depth_component32f = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.texsubimage2d_depth.depth32f_stencil8 = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.basic_copyteximage2d.cube_luminance = FAIL @@ -347,31 +257,6 @@ 1095 WIN : dEQP-GLES3.functional.texture.specification.teximage3d_depth_pbo.depth_component24_2d_array = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.texsubimage3d_depth.depth_component32f_2d_array = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.texsubimage3d_depth.depth32f_stencil8_2d_array = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rgba32i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rgba32ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rgba16i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rgba16ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rgba8i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rgba8ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rgb10_a2ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rgb32i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rgb32ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rgb16i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rgb16ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rgb8i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rgb8ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rg32i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rg32ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rg16i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rg16ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rg8i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.rg8ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.r32i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.r32ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.r16i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.r16ui_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.r8i_cube = FAIL -1095 WIN : dEQP-GLES3.functional.texture.specification.texstorage2d.format.r8ui_cube = FAIL 1095 WIN : dEQP-GLES3.functional.texture.units.2_units.only_2d.3 = FAIL 1095 WIN : dEQP-GLES3.functional.texture.units.2_units.only_2d.5 = FAIL 1095 WIN : dEQP-GLES3.functional.texture.units.2_units.only_2d.7 = FAIL @@ -489,95 +374,6 @@ 1096 WIN : dEQP-GLES3.functional.fragment_ops.blend.fbo_srgb.equation_src_func_dst_func.reverse_subtract_one_minus_constant_alpha_one_minus_constant_color = FAIL 1096 WIN : dEQP-GLES3.functional.fragment_ops.random.* = FAIL 1096 WIN : dEQP-GLES3.functional.fragment_ops.interaction.basic_shader.* = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.renderbuffer.color0.rgb16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.renderbuffer.depth.depth_component32 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.color0.rgb16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.color0.rgb9_e5 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.color0.srgb_unsigned_byte = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.color0.srgb8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.color0.rgb32ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.color0.rgb16ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.color0.rgb8ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.color0.rgb32i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.color0.rgb16i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.color0.rgb8i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.color0.r8_snorm = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.color0.rg8_snorm = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.color0.rgb8_snorm = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.color0.rgba8_snorm = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.stencil.rgb9_e5 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.stencil.srgb_unsigned_byte = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.stencil.srgb8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.stencil.rgb32ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.stencil.rgb16ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.stencil.rgb8ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.stencil.rgb32i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.stencil.rgb16i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.stencil.rgb8i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.stencil.r8_snorm = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.stencil.rg8_snorm = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.stencil.rgb8_snorm = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.stencil.rgba8_snorm = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.depth.rgb9_e5 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.depth.srgb_unsigned_byte = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.depth.srgb8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.depth.rgb32ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.depth.rgb16ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.depth.rgb8ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.depth.rgb32i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.depth.rgb16i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.depth.rgb8i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.depth.r8_snorm = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.depth.rg8_snorm = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.depth.rgb8_snorm = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.renderable.texture.depth.rgba8_snorm = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.size.distinct = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.layer.2darr_1_3 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.layer.2darr_4_15 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.layer.3d_1_15 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.completeness.layer.3d_4_15 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.render.resize.tex2d_rgba16f_stencil_rbo_stencil_index8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.render.resize.tex2d_rgba8_stencil_rbo_stencil_index8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.render.recreate_depth_stencil.tex2d_rgba8_stencil_rbo_stencil_index8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rgba32i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rgba32ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rgba16i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rgba16ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rgba8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rgba8i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rgba8ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.srgb8_alpha8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rgb10_a2 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rgba4 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rgb5_a1 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rgb8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rgb565 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rg32i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rg32ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rg16i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rg16ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rg8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rg8i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rg8ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.r32i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.r32ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.r16i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.r16ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.r8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.r8i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.r8ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rgba32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rgba16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.r11f_g11f_b10f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rg32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.rg16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.r32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.texcube.r16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.tex2darray.rgba32i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.tex2darray.rgba32ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.tex2darray.rgba16i = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.tex2darray.rgba16ui = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.tex2darray.rgba8 = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.tex3d.rgba32i = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.tex3d.rgba16ui = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.tex3d.rgba8i = FAIL @@ -600,160 +396,12 @@ 1097 WIN : dEQP-GLES3.functional.fbo.color.repeated_clear.sample.tex2d.r8 = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.repeated_clear.sample.tex2d.rgba32f = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.repeated_clear.sample.tex2d.rgba16f = FAIL +1097 WIN : dEQP-GLES3.functional.fbo.color.repeated_clear.sample.tex2d.rgb16f = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.repeated_clear.sample.tex2d.r11f_g11f_b10f = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.repeated_clear.sample.tex2d.rg32f = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.repeated_clear.sample.tex2d.rg16f = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.repeated_clear.sample.tex2d.r32f = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.repeated_clear.sample.tex2d.r16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.depth.depth_test_clamp.depth_component32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.depth.depth_test_clamp.depth_component24 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.depth.depth_test_clamp.depth_component16 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.depth.depth_test_clamp.depth32f_stencil8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.depth.depth_test_clamp.depth24_stencil8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.stencil.basic.depth32f_stencil8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.stencil.basic.depth24_stencil8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.stencil.basic.stencil_index8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.stencil.attach.depth_only = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.stencil.attach.stencil_only = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_nearest = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_linear = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_src_x_nearest = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_src_x_linear = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_src_y_nearest = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_src_y_linear = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_dst_x_nearest = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_dst_x_linear = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_dst_y_nearest = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_dst_y_linear = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_src_dst_x_nearest = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_src_dst_x_linear = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_src_dst_y_nearest = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_src_dst_y_linear = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.nearest_consistency_out_of_bounds_mag_reverse_dst_x = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.nearest_consistency_out_of_bounds_mag_reverse_dst_y = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.nearest_consistency_out_of_bounds_mag_reverse_src_dst_x = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.rect.nearest_consistency_out_of_bounds_mag_reverse_src_dst_y = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba8_to_rgb8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba8_to_rgb565 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba8_to_rgba32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba8_to_rgba16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba8_to_r11f_g11f_b10f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba8_to_rg32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba8_to_rg16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba8_to_r32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba8_to_r16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.srgb8_alpha8_to_rgba32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.srgb8_alpha8_to_rgba16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.srgb8_alpha8_to_r11f_g11f_b10f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.srgb8_alpha8_to_rg32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.srgb8_alpha8_to_rg16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.srgb8_alpha8_to_r32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.srgb8_alpha8_to_r16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb10_a2_to_rgba32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb10_a2_to_rgba16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb10_a2_to_r11f_g11f_b10f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb10_a2_to_rg32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb10_a2_to_rg16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb10_a2_to_r32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb10_a2_to_r16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba4_to_rgb8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba4_to_rgb565 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba4_to_rgba32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba4_to_rgba16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba4_to_r11f_g11f_b10f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba4_to_rg32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba4_to_rg16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba4_to_r32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgba4_to_r16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb5_a1_to_rgba32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb5_a1_to_rgba16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb5_a1_to_r11f_g11f_b10f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb5_a1_to_rg32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb5_a1_to_rg16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb5_a1_to_r32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb5_a1_to_r16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb8_to_rgba32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb8_to_rgba16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb8_to_r11f_g11f_b10f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb8_to_rg32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb8_to_rg16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb8_to_r32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb8_to_r16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb565_to_rgba32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb565_to_rgba16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb565_to_r11f_g11f_b10f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb565_to_rg32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb565_to_rg16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb565_to_r32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rgb565_to_r16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rg8_to_rgba32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rg8_to_rgba16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rg8_to_r11f_g11f_b10f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rg8_to_rg32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rg8_to_rg16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rg8_to_r32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.rg8_to_r16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.r8_to_rgba32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.r8_to_rgba16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.r8_to_r11f_g11f_b10f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.r8_to_rg32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.r8_to_rg16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.r8_to_r32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.conversion.r8_to_r16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.depth_stencil.stencil_index8_basic = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.depth_stencil.stencil_index8_scale = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgba8_linear_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgba8_linear_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.srgb8_alpha8_linear_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.srgb8_alpha8_linear_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgb10_a2_linear_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgb10_a2_linear_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgba4_linear_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgba4_linear_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgb5_a1_linear_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgb5_a1_linear_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgb8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgb8_linear_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgb8_linear_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgb565 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgb565_linear_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgb565_linear_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rg8_linear_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rg8_linear_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r8_linear_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r8_linear_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgba32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgba32f_nearest_scale_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgba32f_nearest_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgba32f_nearest_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgba16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgba16f_nearest_scale_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgba16f_linear_scale_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgba16f_linear_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rgba16f_linear_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r11f_g11f_b10f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r11f_g11f_b10f_nearest_scale_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r11f_g11f_b10f_linear_scale_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r11f_g11f_b10f_linear_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r11f_g11f_b10f_linear_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rg32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rg32f_nearest_scale_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rg32f_nearest_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rg32f_nearest_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rg16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rg16f_nearest_scale_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rg16f_linear_scale_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rg16f_linear_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.rg16f_linear_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r32f_nearest_scale_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r32f_nearest_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r32f_nearest_out_of_bounds_blit_to_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r16f_nearest_scale_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r16f_linear_scale_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r16f_linear_out_of_bounds_blit_from_default = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.blit.default_framebuffer.r16f_linear_out_of_bounds_blit_to_default = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.msaa.2_samples.depth_component32f = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.msaa.2_samples.depth_component24 = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.msaa.2_samples.depth_component16 = FAIL @@ -772,7 +420,6 @@ 1097 WIN : dEQP-GLES3.functional.fbo.msaa.8_samples.depth32f_stencil8 = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.msaa.8_samples.depth24_stencil8 = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.msaa.8_samples.stencil_index8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.invalidate.format.stencil_index8 = FAIL 1098 WIN : dEQP-GLES3.functional.uniform_api.info_query.active_uniform.basic_struct.sampler2D_samplerCube_vertex = FAIL 1098 WIN : dEQP-GLES3.functional.uniform_api.info_query.active_uniform.basic_struct.sampler2D_samplerCube_fragment = FAIL 1098 WIN : dEQP-GLES3.functional.uniform_api.info_query.active_uniform.basic_struct.sampler2D_samplerCube_both = FAIL @@ -898,53 +545,20 @@ 1098 WIN : dEQP-GLES3.functional.uniform_api.random.81 = FAIL 1098 WIN : dEQP-GLES3.functional.uniform_api.random.83 = FAIL 1098 WIN : dEQP-GLES3.functional.uniform_api.random.87 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.cond_float = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.max_cond_float = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.cond_vec2 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.max_cond_vec2 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.cond_vec3 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.max_cond_vec3 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.cond_vec4 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.max_cond_vec4 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.cond_mat2 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.cond_mat2_offset_1 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.max_cond_mat2 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.cond_mat3 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.cond_mat3_offset_1 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.max_cond_mat3 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.cond_mat4 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.cond_mat4_offset_1 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.max_cond_mat4 = FAIL 1099 WIN : dEQP-GLES3.functional.attribute_location.bind_hole.mat2 = FAIL 1099 WIN : dEQP-GLES3.functional.attribute_location.bind_hole.mat2x2 = FAIL 1099 WIN : dEQP-GLES3.functional.attribute_location.layout_hole.mat2 = FAIL 1099 WIN : dEQP-GLES3.functional.attribute_location.layout_hole.mat2x2 = FAIL 1099 WIN : dEQP-GLES3.functional.attribute_location.mixed_hole.mat2 = FAIL 1099 WIN : dEQP-GLES3.functional.attribute_location.mixed_hole.mat2x2 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_relink_hole.mat2 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.bind_relink_hole.mat2x2 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.mixed_relink_hole.mat2 = FAIL -1099 WIN : dEQP-GLES3.functional.attribute_location.mixed_relink_hole.mat2x2 = FAIL -1101 WIN : dEQP-GLES3.functional.rasterizer_discard.basic.clear_color = FAIL -1101 WIN : dEQP-GLES3.functional.rasterizer_discard.scissor.clear_color = FAIL -1101 WIN : dEQP-GLES3.functional.rasterizer_discard.fbo.clear_color = FAIL -1101 WIN : dEQP-GLES3.functional.rasterizer_discard.fbo.clear_depth = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.buffer.draw_buffers = FAIL -1101 WIN : dEQP-GLES3.functional.negative_api.buffer.read_buffer = FAIL -1101 WIN : dEQP-GLES3.functional.negative_api.buffer.renderbuffer_storage = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.buffer.renderbuffer_storage_multisample = FAIL -1101 WIN : dEQP-GLES3.functional.negative_api.texture.bindtexture = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.texture.compressedtexsubimage2d = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.texture.compressedtexsubimage2d_invalid_size = FAIL -1101 WIN : dEQP-GLES3.functional.negative_api.texture.teximage3d = FAIL -1101 WIN : dEQP-GLES3.functional.negative_api.texture.texsubimage3d = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.texture.compressedtexsubimage3d = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.texture.compressedtexsubimage3d_invalid_buffer_target = FAIL -1101 WIN : dEQP-GLES3.functional.negative_api.texture.texstorage2d = FAIL -1101 WIN : dEQP-GLES3.functional.negative_api.texture.texstorage3d = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.shader.link_program = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.shader.use_program = FAIL -1101 WIN : dEQP-GLES3.functional.negative_api.shader.program_parameteri = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.shader.sampler_parameterfv = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.shader.gen_transform_feedbacks = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.shader.delete_transform_feedbacks = FAIL @@ -955,56 +569,6 @@ 1101 WIN : dEQP-GLES3.functional.negative_api.vertex_array.draw_range_elements_incomplete_primitive = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.state.get_buffer_pointerv = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.state.get_framebuffer_attachment_parameteriv = FAIL -1101 WIN : dEQP-GLES3.functional.negative_api.state.is_transform_feedback = FAIL -1101 WIN : dEQP-GLES3.functional.negative_api.state.is_vertex_array = FAIL -1101 WIN : dEQP-GLES3.functional.read_pixels.rowlength.choose_17 = FAIL -1101 WIN : dEQP-GLES3.functional.read_pixels.rowlength.choose_19 = FAIL -1101 WIN : dEQP-GLES3.functional.read_pixels.rowlength.choose_23 = FAIL -1101 WIN : dEQP-GLES3.functional.read_pixels.rowlength.choose_29 = FAIL -1101 WIN : dEQP-GLES3.functional.read_pixels.skip.choose_0_3 = FAIL -1101 WIN : dEQP-GLES3.functional.read_pixels.skip.choose_3_0 = FAIL -1101 WIN : dEQP-GLES3.functional.read_pixels.skip.choose_3_3 = FAIL -1101 WIN : dEQP-GLES3.functional.read_pixels.skip.choose_3_5 = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.boolean.rasterizer_discard_getboolean = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.boolean.rasterizer_discard_getinteger = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.boolean.rasterizer_discard_getinteger64 = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.boolean.rasterizer_discard_getfloat = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.max_fragment_uniform_components_getinteger64 = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.max_fragment_uniform_components_getfloat = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.compressed_texture_formats_getinteger = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.compressed_texture_formats_getinteger64 = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.compressed_texture_formats_getfloat = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.uniform_buffer_binding_getboolean = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.uniform_buffer_binding_getinteger = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.uniform_buffer_binding_getinteger64 = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.uniform_buffer_binding_getfloat = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.pixel_pack_buffer_binding_getboolean = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.pixel_pack_buffer_binding_getinteger = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.pixel_pack_buffer_binding_getinteger64 = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.pixel_pack_buffer_binding_getfloat = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.pixel_unpack_buffer_binding_getboolean = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.pixel_unpack_buffer_binding_getinteger = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.pixel_unpack_buffer_binding_getinteger64 = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.pixel_unpack_buffer_binding_getfloat = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.transform_feedback_buffer_binding_getboolean = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.transform_feedback_buffer_binding_getinteger = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.transform_feedback_buffer_binding_getinteger64 = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.transform_feedback_buffer_binding_getfloat = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.copy_read_buffer_binding_getboolean = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.copy_read_buffer_binding_getinteger = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.copy_read_buffer_binding_getinteger64 = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.copy_read_buffer_binding_getfloat = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.copy_write_buffer_binding_getboolean = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.copy_write_buffer_binding_getinteger = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.copy_write_buffer_binding_getinteger64 = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.copy_write_buffer_binding_getfloat = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.element_array_buffer_binding_getboolean = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.element_array_buffer_binding_getinteger = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.element_array_buffer_binding_getinteger64 = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.element_array_buffer_binding_getfloat = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.transform_feedback_binding_getinteger = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.transform_feedback_binding_getinteger64 = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.transform_feedback_binding_getfloat = FAIL 1101 WIN : dEQP-GLES3.functional.state_query.integers.sampler_binding_getboolean = FAIL 1101 WIN : dEQP-GLES3.functional.state_query.integers.sampler_binding_getinteger = FAIL 1101 WIN : dEQP-GLES3.functional.state_query.integers.sampler_binding_getinteger64 = FAIL @@ -1040,24 +604,8 @@ 1101 WIN : dEQP-GLES3.functional.state_query.sampler.sampler_texture_min_lod_getsamplerparameteri = FAIL 1101 WIN : dEQP-GLES3.functional.state_query.sampler.sampler_texture_max_lod_getsamplerparameteri = FAIL 1101 WIN : dEQP-GLES3.functional.state_query.fbo.framebuffer_attachment_x_size_rbo = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.fbo.framebuffer_unspecified_attachment_x_size_rbo = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.rbo.renderbuffer_internal_format = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.rbo.renderbuffer_component_size_color = FAIL 1101 WIN : dEQP-GLES3.functional.state_query.shader.program_attached_shaders = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.shader.program_active_uniform_types = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.shader.program_binary = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.shader.transform_feedback = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.internal_format.rgba_samples = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.internal_format.rgb_samples = FAIL 1101 WIN : dEQP-GLES3.functional.polygon_offset.fixed16_render_with_units = FAIL 1101 WIN : dEQP-GLES3.functional.polygon_offset.fixed24_render_with_units = FAIL -1101 WIN : dEQP-GLES3.functional.flush_finish.flush = FAIL -1101 WIN : dEQP-GLES3.functional.lifetime.gen.transform_feedback = FAIL -1101 WIN : dEQP-GLES3.functional.lifetime.gen.vertex_array = FAIL 1101 WIN : dEQP-GLES3.functional.lifetime.attach.deleted_input.buffer_vertex_array = FAIL -1101 WIN : dEQP-GLES3.functional.lifetime.attach.deleted_name.buffer_vertex_array = FAIL 1101 WIN : dEQP-GLES3.functional.lifetime.delete_active.transform_feedback = FAIL -1101 WIN : dEQP-GLES3.functional.lifetime.delete_bound.transform_feedback = SKIP - -// TODO(jmadill): Find why this fails when run in a certain sequence, but not singly. -1098 WIN : dEQP-GLES3.functional.uniform_api.random.50 = FAIL diff --git a/gfx/angle/src/tests/deqp_support/generate_case_lists.py b/gfx/angle/src/tests/deqp_support/generate_case_lists.py deleted file mode 100644 index c2da15753b..0000000000 --- a/gfx/angle/src/tests/deqp_support/generate_case_lists.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2015 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. -# -# generate_case_lists.py: -# Helper script for updating the dEQP case list files, stored in the repo. -# Generally only used when the dEQP config changes, or when we roll dEQP. - -import subprocess -import sys -import os -import shutil -import gzip - -# TODO(jmadill): other platforms -os_suffix = '.exe' -build_dir = os.path.join('build', 'Debug_x64') - -def run_deqp(deqp_exe): - subprocess.call([deqp_exe, '--deqp-runmode=txt-caselist', '--deqp-gl-context-type=null']) - -# This stuff is all hard-coded for now. If we need more versatility we can -# make some options into command line arguments with default values. -script_dir = os.path.dirname(sys.argv[0]) -path_to_deqp_exe = os.path.join('..', '..', build_dir) -deqp_data_path = os.path.join('third_party', 'deqp', 'data') - -os.chdir(os.path.join(script_dir, '..')) -run_deqp(os.path.join(path_to_deqp_exe, 'angle_deqp_gles2_tests' + os_suffix)) -run_deqp(os.path.join(path_to_deqp_exe, 'angle_deqp_gles3_tests' + os_suffix)) -run_deqp(os.path.join(path_to_deqp_exe, 'angle_deqp_egl_tests' + os_suffix)) - -def compress_case_list(case_file): - with open(os.path.join(deqp_data_path, case_file + '.txt')) as in_handle: - data = in_handle.read() - in_handle.close() - with gzip.open(os.path.join('deqp_support', case_file + '.txt.gz'), 'wb') as out_handle: - out_handle.write(data) - out_handle.close() - -compress_case_list('dEQP-GLES2-cases') -compress_case_list('dEQP-GLES3-cases') -compress_case_list('dEQP-EGL-cases') diff --git a/gfx/angle/src/tests/deqp_tests/deqp_test_main.cpp b/gfx/angle/src/tests/deqp_tests/deqp_test_main.cpp deleted file mode 100644 index 487a2ba92e..0000000000 --- a/gfx/angle/src/tests/deqp_tests/deqp_test_main.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright (c) 2014 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. -// - -#include "gtest/gtest.h" -#include "deqp_tests.h" - -#include - -#include -#include -#include - -int main(int argc, char** argv) -{ - testing::InitGoogleTest(&argc, argv); - - typedef std::pair NameDisplayTypePair; - typedef std::map DisplayTypeMap; - DisplayTypeMap allDisplays; - allDisplays["d3d11"] = EGL_D3D11_ONLY_DISPLAY_ANGLE; - - // Iterate through the command line arguments and check if they are config names - std::vector requestedDisplays; - for (size_t i = 1; i < static_cast(argc); i++) - { - DisplayTypeMap::const_iterator iter = allDisplays.find(argv[i]); - if (iter != allDisplays.end()) - { - requestedDisplays.push_back(*iter); - } - } - - // If no configs were requested, run them all - if (requestedDisplays.empty()) - { - for (DisplayTypeMap::const_iterator i = allDisplays.begin(); i != allDisplays.end(); i++) - { - requestedDisplays.push_back(*i); - } - } - - // Run each requested config - int rt = 0; - for (size_t i = 0; i < requestedDisplays.size(); i++) - { - DEQPConfig config; - config.displayType = requestedDisplays[i].second; - config.width = 256; - config.height = 256; - config.hidden = false; - - SetCurrentConfig(config); - - std::cout << "Running test configuration \"" << requestedDisplays[i].first << "\".\n"; - - rt |= RUN_ALL_TESTS(); - } - - return rt; -} diff --git a/gfx/angle/src/tests/deqp_tests/deqp_tests.cpp b/gfx/angle/src/tests/deqp_tests/deqp_tests.cpp deleted file mode 100644 index 6fd46e1043..0000000000 --- a/gfx/angle/src/tests/deqp_tests/deqp_tests.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// -// Copyright (c) 2014 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. -// - -#include "deqp_tests.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include "tcuDefs.hpp" -#include "tcuCommandLine.hpp" -#include "tcuPlatform.hpp" -#include "tcuApp.hpp" -#include "tcuResource.hpp" -#include "tcuTestLog.hpp" -#include "tcuTestExecutor.hpp" -#include "deUniquePtr.hpp" -#include "tes3TestPackage.hpp" - -#include "win32/tcuWin32EglPlatform.hpp" - -// Register the gles3 test cases -static tcu::TestPackage* createTestPackage(tcu::TestContext& testCtx) -{ - return new deqp::gles3::TestPackage(testCtx); -} -tcu::TestPackageDescriptor g_gles3PackageDescriptor("dEQP-GLES3", createTestPackage); - -// Create a platform that supports custom display types -class Win32EglCustomDisplayPlatform : public tcu::Win32EglPlatform -{ -public: - Win32EglCustomDisplayPlatform(EGLNativeDisplayType displayType) - : mDisplayType(displayType) - { - } - - virtual ~Win32EglCustomDisplayPlatform() - { - } - - virtual tcu::NativeDisplay* createDefaultDisplay() - { - return new tcu::Win32EglDisplay(mDisplayType); - } - -private: - EGLNativeDisplayType mDisplayType; -}; - -static std::vector FormatArg(const char* fmt, ...) -{ - va_list vararg; - va_start(vararg, fmt); - int len = vsnprintf(NULL, 0, fmt, vararg); - va_end(vararg); - - std::vector buf(len + 1); - - va_start(vararg, fmt); - vsnprintf(buf.data(), buf.size(), fmt, vararg); - va_end(vararg); - - return buf; -} - -static std::string GetExecutableDirectory() -{ - std::vector executableFileBuf(MAX_PATH); - DWORD executablePathLen = GetModuleFileNameA(NULL, executableFileBuf.data(), executableFileBuf.size()); - if (executablePathLen == 0) - { - return false; - } - - std::string executableLocation = executableFileBuf.data(); - size_t lastPathSepLoc = executableLocation.find_last_of("\\/"); - if (lastPathSepLoc != std::string::npos) - { - executableLocation = executableLocation.substr(0, lastPathSepLoc); - } - else - { - executableLocation = ""; - } - - return executableLocation; -} - -static DEQPConfig kCurrentConfig = { 256, 256, false, EGL_D3D11_ONLY_DISPLAY_ANGLE }; - -void SetCurrentConfig(const DEQPConfig& config) -{ - kCurrentConfig = config; -} - -const DEQPConfig& GetCurrentConfig() -{ - return kCurrentConfig; -} - -void RunDEQPTest(const std::string &testPath, const DEQPConfig& config) -{ - try - { - std::vector args; - - // Empty first argument for the program name - args.push_back("deqp-gles3"); - - std::vector visibilityArg = FormatArg("--deqp-visibility=%s", config.hidden ? "hidden" : "windowed"); - args.push_back(visibilityArg.data()); - - std::vector widthArg = FormatArg("--deqp-surface-width=%u", config.width); - args.push_back(widthArg.data()); - - std::vector heightArg = FormatArg("--deqp-surface-height=%u", config.height); - args.push_back(heightArg.data()); - - std::vector testNameArg = FormatArg("--deqp-case=%s", testPath.c_str()); - args.push_back(testNameArg.data()); - - // Redirect cout - std::streambuf* oldCoutStreamBuf = std::cout.rdbuf(); - std::ostringstream strCout; - std::cout.rdbuf(strCout.rdbuf()); - - tcu::CommandLine cmdLine(args.size(), args.data()); - tcu::DirArchive archive(GetExecutableDirectory().c_str()); - tcu::TestLog log(cmdLine.getLogFileName(), cmdLine.getLogFlags()); - de::UniquePtr platform(new Win32EglCustomDisplayPlatform(config.displayType)); - de::UniquePtr app(new tcu::App(*platform, archive, log, cmdLine)); - - // Main loop. - for (;;) - { - if (!app->iterate()) - { - break; - } - } - - // Restore old cout - std::cout.rdbuf(oldCoutStreamBuf); - - EXPECT_EQ(0, app->getResult().numFailed) << strCout.str(); - } - catch (const std::exception& e) - { - FAIL() << e.what(); - } -} diff --git a/gfx/angle/src/tests/deqp_tests/deqp_tests.h b/gfx/angle/src/tests/deqp_tests/deqp_tests.h deleted file mode 100644 index 59be42523c..0000000000 --- a/gfx/angle/src/tests/deqp_tests/deqp_tests.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// Copyright (c) 2014 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 DEQP_TESTS_DEQP_TESTS_H_ -#define DEQP_TESTS_DEQP_TESTS_H_ - -#include "gtest/gtest.h" - -#include - -#include - -struct DEQPConfig -{ - size_t width; - size_t height; - bool hidden; - EGLNativeDisplayType displayType; -}; - -void SetCurrentConfig(const DEQPConfig& config); -const DEQPConfig& GetCurrentConfig(); - -void RunDEQPTest(const std::string &testPath, const DEQPConfig& config); - -#endif // DEQP_TESTS_DEQP_TESTS_H_ diff --git a/gfx/angle/src/tests/deqp_tests/deqp_tests.txt b/gfx/angle/src/tests/deqp_tests/deqp_tests.txt deleted file mode 100644 index 149b47c7a0..0000000000 --- a/gfx/angle/src/tests/deqp_tests/deqp_tests.txt +++ /dev/null @@ -1,40 +0,0 @@ -dEQP-GLES3.functional.prerequisite.* -dEQP-GLES3.functional.buffer.write.* -dEQP-GLES3.functional.samplers.* -dEQP-GLES3.functional.state_query.internal_format.* -dEQP-GLES3.functional.shaders.preprocessor.basic.* -dEQP-GLES3.functional.shaders.preprocessor.invalid_redefinitions.* -dEQP-GLES3.functional.shaders.preprocessor.comments.* -dEQP-GLES3.functional.shaders.preprocessor.function_definitions.* -dEQP-GLES3.functional.shaders.preprocessor.recursion.* -dEQP-GLES3.functional.shaders.preprocessor.function_redefinitions.* -dEQP-GLES3.functional.shaders.preprocessor.semantic.* -dEQP-GLES3.functional.shaders.preprocessor.invalid_ops.* -dEQP-GLES3.functional.shaders.preprocessor.directive.* -dEQP-GLES3.functional.shaders.preprocessor.extensions.* -dEQP-GLES3.functional.shaders.preprocessor.invalid_expressions.* -dEQP-GLES3.functional.shaders.preprocessor.operator_precedence.* -dEQP-GLES3.functional.shaders.constants.* -dEQP-GLES3.functional.shaders.linkage.varying.interpolation.* -dEQP-GLES3.functional.shaders.linkage.varying.usage.* -dEQP-GLES3.functional.shaders.conversions.scalar_to_scalar.* -dEQP-GLES3.functional.shaders.conversions.scalar_to_vector.* -dEQP-GLES3.functional.shaders.conversions.vector_to_scalar.* -dEQP-GLES3.functional.shaders.conversions.vector_illegal.* -dEQP-GLES3.functional.shaders.conversions.vector_to_vector.* -dEQP-GLES3.functional.texture.mipmap.2d.basic.* -dEQP-GLES3.functional.texture.mipmap.2d.affine.* -dEQP-GLES3.functional.texture.mipmap.2d.bias.* -dEQP-GLES3.functional.texture.mipmap.cube.projected.* -dEQP-GLES3.functional.texture.mipmap.cube.bias.* -dEQP-GLES3.functional.texture.mipmap.3d.basic.* -dEQP-GLES3.functional.texture.mipmap.3d.affine.* -dEQP-GLES3.functional.texture.mipmap.3d.projected.* -dEQP-GLES3.functional.texture.mipmap.3d.bias.* -dEQP-GLES3.functional.multisample.* -dEQP-GLES3.functional.shaders.linkage.varying.basic_types.* -dEQP-GLES3.functional.shaders.linkage.varying.struct.* -dEQP-GLES3.functional.shaders.linkage.varying.interpolation.* -dEQP-GLES3.functional.shaders.linkage.varying.usage.* -dEQP-GLES3.functional.shaders.linkage.uniform.* -dEQP-GLES3.functional.rasterizer_discard.* \ No newline at end of file diff --git a/gfx/angle/src/tests/deqp_tests/generate_deqp_tests.py b/gfx/angle/src/tests/deqp_tests/generate_deqp_tests.py deleted file mode 100644 index 19ff24b785..0000000000 --- a/gfx/angle/src/tests/deqp_tests/generate_deqp_tests.py +++ /dev/null @@ -1,44 +0,0 @@ -import os -import re -import sys - -def ReadFileAsLines(filename): - """Reads a file, removing blank lines and lines that start with #""" - file = open(filename, "r") - raw_lines = file.readlines() - file.close() - lines = [] - for line in raw_lines: - line = line.strip() - if len(line) > 0 and not line.startswith("#"): - lines.append(line) - return lines - -def GetCleanTestName(testName): - replacements = { "dEQP-": "", ".*": "", ".":"_", } - cleanName = testName - for replaceKey in replacements: - cleanName = cleanName.replace(replaceKey, replacements[replaceKey]) - return cleanName - -def GenerateTests(outFile, testNames): - ''' Remove duplicate tests ''' - testNames = list(set(testNames)) - - outFile.write("#include \"deqp_tests.h\"\n\n") - - for test in testNames: - outFile.write("TEST(deqp, " + GetCleanTestName(test) + ")\n") - outFile.write("{\n") - outFile.write(" RunDEQPTest(\"" + test + "\", GetCurrentConfig());\n") - outFile.write("}\n\n") - -def main(argv): - tests = ReadFileAsLines(argv[0]) - output = open(argv[1], 'wb') - GenerateTests(output, tests) - output.close() - return 0 - -if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) diff --git a/gfx/angle/src/tests/egl_tests/EGLDeviceTest.cpp b/gfx/angle/src/tests/egl_tests/EGLDeviceTest.cpp new file mode 100644 index 0000000000..7217541813 --- /dev/null +++ b/gfx/angle/src/tests/egl_tests/EGLDeviceTest.cpp @@ -0,0 +1,587 @@ +// +// Copyright 2015 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 + +#include "com_utils.h" +#include "OSWindow.h" +#include "test_utils/ANGLETest.h" + +using namespace angle; + +class EGLDeviceCreationTest : public testing::Test +{ + protected: + EGLDeviceCreationTest() + : mD3D11Available(false), + mD3D11Module(nullptr), + mD3D11CreateDevice(nullptr), + mDevice(nullptr), + mDeviceContext(nullptr), + mDeviceCreationD3D11ExtAvailable(false), + mOSWindow(nullptr), + mDisplay(EGL_NO_DISPLAY), + mSurface(EGL_NO_SURFACE), + mContext(EGL_NO_CONTEXT), + mConfig(0) + { + } + + void SetUp() override + { + mD3D11Module = LoadLibrary(TEXT("d3d11.dll")); + if (mD3D11Module == nullptr) + { + std::cout << "Unable to LoadLibrary D3D11" << std::endl; + return; + } + + mD3D11CreateDevice = reinterpret_cast( + 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(eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS)); + if (strstr(extensionString, "EGL_ANGLE_device_creation")) + { + if (strstr(extensionString, "EGL_ANGLE_device_creation_d3d11")) + { + mDeviceCreationD3D11ExtAvailable = true; + } + } + } + + 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)); + ASSERT_GE(mFeatureLevel, D3D_FEATURE_LEVEL_9_3); + } + + void CreateD3D11FL9_3Device() + { + ASSERT_TRUE(mD3D11Available); + ASSERT_EQ(nullptr, mDevice); + + D3D_FEATURE_LEVEL fl93 = D3D_FEATURE_LEVEL_9_3; + + HRESULT hr = + mD3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, 0, &fl93, 1, D3D11_SDK_VERSION, + &mDevice, &mFeatureLevel, &mDeviceContext); + + ASSERT_TRUE(SUCCEEDED(hr)); + } + + void CreateWindowSurface() + { + EGLint majorVersion, minorVersion; + ASSERT_EGL_TRUE(eglInitialize(mDisplay, &majorVersion, &minorVersion)); + + 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)); + + // Create an OS Window + mOSWindow = CreateOSWindow(); + mOSWindow->initialize("EGLSurfaceTest", 64, 64); + + // Create window surface + mSurface = eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(), nullptr); + ASSERT_EGL_SUCCESS(); + + // 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(); + } + + // This triggers a D3D device lost on current Windows systems + // This behavior could potentially change in the future + void trigger9_3DeviceLost() + { + ID3D11Buffer *gsBuffer = nullptr; + D3D11_BUFFER_DESC bufferDesc = {0}; + bufferDesc.ByteWidth = 64; + bufferDesc.Usage = D3D11_USAGE_DEFAULT; + bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + + HRESULT result = mDevice->CreateBuffer(&bufferDesc, nullptr, &gsBuffer); + ASSERT_TRUE(SUCCEEDED(result)); + + mDeviceContext->GSSetConstantBuffers(0, 1, &gsBuffer); + SafeRelease(gsBuffer); + gsBuffer = nullptr; + + result = mDevice->GetDeviceRemovedReason(); + ASSERT_TRUE(FAILED(result)); + } + + bool mD3D11Available; + HMODULE mD3D11Module; + PFN_D3D11_CREATE_DEVICE mD3D11CreateDevice; + + ID3D11Device *mDevice; + ID3D11DeviceContext *mDeviceContext; + D3D_FEATURE_LEVEL mFeatureLevel; + + bool mDeviceCreationD3D11ExtAvailable; + + OSWindow *mOSWindow; + + EGLDisplay mDisplay; + EGLSurface mSurface; + EGLContext mContext; + EGLConfig mConfig; +}; + +// Test that creating a EGLDeviceEXT from D3D11 device works, and it can be queried to retrieve +// D3D11 device +TEST_F(EGLDeviceCreationTest, BasicD3D11Device) +{ + if (!mDeviceCreationD3D11ExtAvailable || !mD3D11Available) + { + std::cout << "EGLDevice creation and/or D3D11 not available, skipping test" << std::endl; + return; + } + + CreateD3D11Device(); + + EGLDeviceEXT eglDevice = + eglCreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE, reinterpret_cast(mDevice), nullptr); + ASSERT_NE(EGL_NO_DEVICE_EXT, eglDevice); + ASSERT_EGL_SUCCESS(); + + EGLAttrib deviceAttrib; + eglQueryDeviceAttribEXT(eglDevice, EGL_D3D11_DEVICE_ANGLE, &deviceAttrib); + ASSERT_EGL_SUCCESS(); + + ID3D11Device *queriedDevice = reinterpret_cast(deviceAttrib); + ASSERT_EQ(mFeatureLevel, queriedDevice->GetFeatureLevel()); + + eglReleaseDeviceANGLE(eglDevice); +} + +// Test that creating a EGLDeviceEXT from D3D11 device works, and it can be queried to retrieve +// D3D11 device +TEST_F(EGLDeviceCreationTest, BasicD3D11DeviceViaFuncPointer) +{ + if (!mDeviceCreationD3D11ExtAvailable || !mD3D11Available) + { + std::cout << "EGLDevice creation and/or D3D11 not available, skipping test" << std::endl; + return; + } + + CreateD3D11Device(); + + PFNEGLCREATEDEVICEANGLEPROC createDeviceANGLE = + (PFNEGLCREATEDEVICEANGLEPROC)eglGetProcAddress("eglCreateDeviceANGLE"); + PFNEGLRELEASEDEVICEANGLEPROC releaseDeviceANGLE = + (PFNEGLRELEASEDEVICEANGLEPROC)eglGetProcAddress("eglReleaseDeviceANGLE"); + + EGLDeviceEXT eglDevice = + createDeviceANGLE(EGL_D3D11_DEVICE_ANGLE, reinterpret_cast(mDevice), nullptr); + ASSERT_NE(EGL_NO_DEVICE_EXT, eglDevice); + ASSERT_EGL_SUCCESS(); + + EGLAttrib deviceAttrib; + eglQueryDeviceAttribEXT(eglDevice, EGL_D3D11_DEVICE_ANGLE, &deviceAttrib); + ASSERT_EGL_SUCCESS(); + + ID3D11Device *queriedDevice = reinterpret_cast(deviceAttrib); + ASSERT_EQ(mFeatureLevel, queriedDevice->GetFeatureLevel()); + + releaseDeviceANGLE(eglDevice); +} + +// Test that creating a EGLDeviceEXT from D3D11 device works, and can be used for rendering +TEST_F(EGLDeviceCreationTest, RenderingUsingD3D11Device) +{ + if (!mD3D11Available) + { + std::cout << "D3D11 not available, skipping test" << std::endl; + return; + } + + CreateD3D11Device(); + + EGLDeviceEXT eglDevice = + eglCreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE, reinterpret_cast(mDevice), nullptr); + ASSERT_EGL_SUCCESS(); + + // Create an EGLDisplay using the EGLDevice + mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, eglDevice, nullptr); + ASSERT_NE(EGL_NO_DISPLAY, mDisplay); + + // Create a surface using the display + CreateWindowSurface(); + + // Perform some very basic rendering + glClearColor(1.0f, 0.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_PIXEL_EQ(32, 32, 255, 0, 255, 255); + + // Note that we must call TearDown() before we release the EGL device, since the display + // depends on the device + TearDown(); + + eglReleaseDeviceANGLE(eglDevice); +} + +// Test that ANGLE doesn't try to recreate a D3D11 device if the inputted one is lost +TEST_F(EGLDeviceCreationTest, D3D11DeviceRecovery) +{ + if (!mD3D11Available) + { + std::cout << "D3D11 not available, skipping test" << std::endl; + return; + } + + // Force Feature Level 9_3 so we can easily trigger a device lost later + CreateD3D11FL9_3Device(); + + EGLDeviceEXT eglDevice = + eglCreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE, reinterpret_cast(mDevice), nullptr); + ASSERT_EGL_SUCCESS(); + + // Create an EGLDisplay using the EGLDevice + mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, eglDevice, nullptr); + ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY); + + // Create a surface using the display + CreateWindowSurface(); + + // Perform some very basic rendering + glClearColor(1.0f, 0.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_PIXEL_EQ(32, 32, 255, 0, 255, 255); + ASSERT_GL_NO_ERROR(); + + // ANGLE's SwapChain11::initPassThroughResources doesn't handle device lost before + // eglSwapBuffers, so we must call eglSwapBuffers before we lose the device. + ASSERT_EGL_TRUE(eglSwapBuffers(mDisplay, mSurface)); + + // Trigger a lost device + trigger9_3DeviceLost(); + + // Destroy the old EGL Window Surface + if (mSurface != EGL_NO_SURFACE) + { + eglDestroySurface(mDisplay, mSurface); + mSurface = EGL_NO_SURFACE; + } + + // Try to create a new window surface. In certain configurations this will recreate the D3D11 + // device. We want to test that it doesn't recreate the D3D11 device when EGLDeviceEXT is + // used. The window surface creation should fail if a new D3D11 device isn't created. + mSurface = eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(), nullptr); + ASSERT_EQ(EGL_NO_SURFACE, mSurface); + ASSERT_EGL_ERROR(EGL_BAD_ALLOC); + + // Get the D3D11 device out of the EGLDisplay again. It should be the same one as above. + EGLAttrib device = 0; + EGLAttrib newEglDevice = 0; + ASSERT_EGL_TRUE(eglQueryDisplayAttribEXT(mDisplay, EGL_DEVICE_EXT, &newEglDevice)); + ASSERT_EGL_TRUE(eglQueryDeviceAttribEXT(reinterpret_cast(newEglDevice), + EGL_D3D11_DEVICE_ANGLE, &device)); + ID3D11Device *newDevice = reinterpret_cast(device); + + ASSERT_EQ(reinterpret_cast(newEglDevice), eglDevice); + ASSERT_EQ(newDevice, mDevice); + + // Note that we must call TearDown() before we release the EGL device, since the display + // depends on the device + TearDown(); + + eglReleaseDeviceANGLE(eglDevice); +} + +// Test that calling eglGetPlatformDisplayEXT with the same device returns the same display +TEST_F(EGLDeviceCreationTest, getPlatformDisplayTwice) +{ + if (!mD3D11Available) + { + std::cout << "D3D11 not available, skipping test" << std::endl; + return; + } + + CreateD3D11Device(); + + EGLDeviceEXT eglDevice = + eglCreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE, reinterpret_cast(mDevice), nullptr); + ASSERT_EGL_SUCCESS(); + + // Create an EGLDisplay using the EGLDevice + EGLDisplay display1 = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, eglDevice, nullptr); + ASSERT_NE(EGL_NO_DISPLAY, display1); + + EGLDisplay display2 = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, eglDevice, nullptr); + ASSERT_NE(EGL_NO_DISPLAY, display2); + + ASSERT_EQ(display1, display2); + + eglTerminate(display1); + eglReleaseDeviceANGLE(eglDevice); +} + +// Test that creating a EGLDeviceEXT from an invalid D3D11 device fails +TEST_F(EGLDeviceCreationTest, InvalidD3D11Device) +{ + if (!mDeviceCreationD3D11ExtAvailable || !mD3D11Available) + { + std::cout << "EGLDevice creation and/or D3D11 not available, skipping test" << std::endl; + return; + } + + CreateD3D11Device(); + + // Use mDeviceContext instead of mDevice + EGLDeviceEXT eglDevice = eglCreateDeviceANGLE( + EGL_D3D11_DEVICE_ANGLE, reinterpret_cast(mDeviceContext), nullptr); + EXPECT_EQ(EGL_NO_DEVICE_EXT, eglDevice); + EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE); +} + +// Test that EGLDeviceEXT holds a ref to the D3D11 device +TEST_F(EGLDeviceCreationTest, D3D11DeviceReferenceCounting) +{ + if (!mDeviceCreationD3D11ExtAvailable || !mD3D11Available) + { + std::cout << "EGLDevice creation and/or D3D11 not available, skipping test" << std::endl; + return; + } + + CreateD3D11Device(); + + EGLDeviceEXT eglDevice = + eglCreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE, reinterpret_cast(mDevice), nullptr); + ASSERT_NE(EGL_NO_DEVICE_EXT, eglDevice); + ASSERT_EGL_SUCCESS(); + + // Now release our D3D11 device/context + SafeRelease(mDevice); + SafeRelease(mDeviceContext); + + EGLAttrib deviceAttrib; + eglQueryDeviceAttribEXT(eglDevice, EGL_D3D11_DEVICE_ANGLE, &deviceAttrib); + ASSERT_EGL_SUCCESS(); + + ID3D11Device *queriedDevice = reinterpret_cast(deviceAttrib); + ASSERT_EQ(mFeatureLevel, queriedDevice->GetFeatureLevel()); + + eglReleaseDeviceANGLE(eglDevice); +} + +// Test that creating a EGLDeviceEXT from a D3D9 device fails +TEST_F(EGLDeviceCreationTest, AnyD3D9Device) +{ + if (!mDeviceCreationD3D11ExtAvailable) + { + std::cout << "EGLDevice creation not available, skipping test" << std::endl; + return; + } + + std::string fakeD3DDevice = "This is a string, not a D3D device"; + + EGLDeviceEXT eglDevice = eglCreateDeviceANGLE( + EGL_D3D9_DEVICE_ANGLE, reinterpret_cast(&fakeD3DDevice), nullptr); + EXPECT_EQ(EGL_NO_DEVICE_EXT, eglDevice); + EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE); +} + +class EGLDeviceQueryTest : public ANGLETest +{ + protected: + EGLDeviceQueryTest() + { + mQueryDisplayAttribEXT = nullptr; + mQueryDeviceAttribEXT = nullptr; + mQueryDeviceStringEXT = nullptr; + } + + void SetUp() override + { + ANGLETest::SetUp(); + + const char *extensionString = + static_cast(eglQueryString(getEGLWindow()->getDisplay(), EGL_EXTENSIONS)); + if (strstr(extensionString, "EGL_EXT_device_query")) + { + mQueryDisplayAttribEXT = + (PFNEGLQUERYDISPLAYATTRIBEXTPROC)eglGetProcAddress("eglQueryDisplayAttribEXT"); + mQueryDeviceAttribEXT = + (PFNEGLQUERYDEVICEATTRIBEXTPROC)eglGetProcAddress("eglQueryDeviceAttribEXT"); + mQueryDeviceStringEXT = + (PFNEGLQUERYDEVICESTRINGEXTPROC)eglGetProcAddress("eglQueryDeviceStringEXT"); + } + + if (!mQueryDeviceStringEXT) + { + FAIL() << "ANGLE extension EGL_EXT_device_query export eglQueryDeviceStringEXT was not " + "found"; + } + + if (!mQueryDisplayAttribEXT) + { + FAIL() << "ANGLE extension EGL_EXT_device_query export eglQueryDisplayAttribEXT was " + "not found"; + } + + if (!mQueryDeviceAttribEXT) + { + FAIL() << "ANGLE extension EGL_EXT_device_query export eglQueryDeviceAttribEXT was not " + "found"; + } + + EGLAttrib angleDevice = 0; + EXPECT_EGL_TRUE( + mQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); + extensionString = static_cast( + mQueryDeviceStringEXT(reinterpret_cast(angleDevice), EGL_EXTENSIONS)); + if (strstr(extensionString, "EGL_ANGLE_device_d3d") == NULL) + { + FAIL() << "ANGLE extension EGL_ANGLE_device_d3d was not found"; + } + } + + void TearDown() override { ANGLETest::TearDown(); } + + PFNEGLQUERYDISPLAYATTRIBEXTPROC mQueryDisplayAttribEXT; + PFNEGLQUERYDEVICEATTRIBEXTPROC mQueryDeviceAttribEXT; + PFNEGLQUERYDEVICESTRINGEXTPROC mQueryDeviceStringEXT; +}; + +// This test attempts to obtain a D3D11 device and a D3D9 device using the eglQueryDeviceAttribEXT +// function. +// If the test is configured to use D3D11 then it should succeed to obtain a D3D11 device. +// If the test is confitured to use D3D9, then it should succeed to obtain a D3D9 device. +TEST_P(EGLDeviceQueryTest, QueryDevice) +{ + EGLAttrib device = 0; + EGLAttrib angleDevice = 0; + if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) + { + EXPECT_EGL_TRUE( + mQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); + EXPECT_EGL_TRUE(mQueryDeviceAttribEXT(reinterpret_cast(angleDevice), + EGL_D3D11_DEVICE_ANGLE, &device)); + ID3D11Device *d3d11Device = reinterpret_cast(device); + IDXGIDevice *dxgiDevice = DynamicCastComObject(d3d11Device); + EXPECT_TRUE(dxgiDevice != nullptr); + SafeRelease(dxgiDevice); + } + + if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE) + { + EXPECT_EGL_TRUE( + mQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); + EXPECT_EGL_TRUE(mQueryDeviceAttribEXT(reinterpret_cast(angleDevice), + EGL_D3D9_DEVICE_ANGLE, &device)); + IDirect3DDevice9 *d3d9Device = reinterpret_cast(device); + IDirect3D9 *d3d9 = nullptr; + EXPECT_EQ(S_OK, d3d9Device->GetDirect3D(&d3d9)); + EXPECT_TRUE(d3d9 != nullptr); + SafeRelease(d3d9); + } +} + +// This test attempts to obtain a D3D11 device from a D3D9 configured system and a D3D9 device from +// a D3D11 configured system using the eglQueryDeviceAttribEXT function. +// If the test is configured to use D3D11 then it should fail to obtain a D3D11 device. +// If the test is confitured to use D3D9, then it should fail to obtain a D3D9 device. +TEST_P(EGLDeviceQueryTest, QueryDeviceBadAttribute) +{ + EGLAttrib device = 0; + EGLAttrib angleDevice = 0; + if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) + { + EXPECT_EGL_TRUE( + mQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); + EXPECT_EGL_FALSE(mQueryDeviceAttribEXT(reinterpret_cast(angleDevice), + EGL_D3D9_DEVICE_ANGLE, &device)); + } + + if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE) + { + EXPECT_EGL_TRUE( + mQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); + EXPECT_EGL_FALSE(mQueryDeviceAttribEXT(reinterpret_cast(angleDevice), + EGL_D3D11_DEVICE_ANGLE, &device)); + } +} + +// Ensure that: +// - calling getPlatformDisplayEXT using ANGLE_Platform with some parameters +// - extracting the EGLDeviceEXT from the EGLDisplay +// - calling getPlatformDisplayEXT with this EGLDeviceEXT +// results in the same EGLDisplay being returned from getPlatformDisplayEXT both times +TEST_P(EGLDeviceQueryTest, getPlatformDisplayDeviceReuse) +{ + EGLAttrib eglDevice = 0; + EXPECT_EGL_TRUE( + eglQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &eglDevice)); + + EGLDisplay display2 = eglGetPlatformDisplayEXT( + EGL_PLATFORM_DEVICE_EXT, reinterpret_cast(eglDevice), nullptr); + EXPECT_EQ(getEGLWindow()->getDisplay(), display2); +} + +// Use this to select which configurations (e.g. which renderer, which GLES major version) these +// tests should be run against. +ANGLE_INSTANTIATE_TEST(EGLDeviceQueryTest, ES2_D3D9(), ES2_D3D11()); diff --git a/gfx/angle/src/tests/egl_tests/EGLPresentPathD3D11Test.cpp b/gfx/angle/src/tests/egl_tests/EGLPresentPathD3D11Test.cpp new file mode 100644 index 0000000000..0b897ab772 --- /dev/null +++ b/gfx/angle/src/tests/egl_tests/EGLPresentPathD3D11Test.cpp @@ -0,0 +1,378 @@ +// +// Copyright 2015 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. +// + +#include "test_utils/ANGLETest.h" + +#include +#include "com_utils.h" +#include "OSWindow.h" +#include + +using namespace angle; + +class EGLPresentPathD3D11 : public testing::TestWithParam +{ + protected: + EGLPresentPathD3D11() + : mDisplay(EGL_NO_DISPLAY), + mContext(EGL_NO_CONTEXT), + mSurface(EGL_NO_SURFACE), + mOffscreenSurfaceD3D11Texture(nullptr), + mConfig(0), + mOSWindow(nullptr), + mWindowWidth(0) + { + } + + void SetUp() override + { + mOSWindow = CreateOSWindow(); + mWindowWidth = 64; + mOSWindow->initialize("EGLPresentPathD3D11", mWindowWidth, mWindowWidth); + } + + void initializeEGL(bool usePresentPathFast) + { + int clientVersion = GetParam().majorVersion; + + const char *extensionString = + static_cast(eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS)); + ASSERT_NE(nullptr, strstr(extensionString, "EGL_ANGLE_experimental_present_path")); + + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = + reinterpret_cast( + eglGetProcAddress("eglGetPlatformDisplayEXT")); + ASSERT_NE(nullptr, eglGetPlatformDisplayEXT); + + // Set up EGL Display + EGLint displayAttribs[] = { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), + EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, GetParam().eglParameters.majorVersion, + EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, GetParam().eglParameters.majorVersion, + EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE, + usePresentPathFast ? EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE + : EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE, + EGL_NONE}; + mDisplay = + eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, displayAttribs); + ASSERT_TRUE(EGL_NO_DISPLAY != mDisplay); + ASSERT_EGL_TRUE(eglInitialize(mDisplay, NULL, NULL)); + + // Choose the EGL config + EGLint numConfigs; + EGLint configAttribs[] = {EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, + EGL_RENDERABLE_TYPE, + clientVersion == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, + EGL_PBUFFER_BIT, + EGL_NONE}; + ASSERT_EGL_TRUE(eglChooseConfig(mDisplay, configAttribs, &mConfig, 1, &numConfigs)); + ASSERT_EQ(1, numConfigs); + + // Set up the EGL context + EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, clientVersion, EGL_NONE}; + mContext = eglCreateContext(mDisplay, mConfig, NULL, contextAttribs); + ASSERT_TRUE(EGL_NO_CONTEXT != mContext); + } + + void createWindowSurface() + { + mSurface = eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(), nullptr); + } + + void createPbufferFromClientBufferSurface() + { + EGLAttrib device = 0; + EGLAttrib angleDevice = 0; + + PFNEGLQUERYDISPLAYATTRIBEXTPROC queryDisplayAttribEXT; + PFNEGLQUERYDEVICEATTRIBEXTPROC queryDeviceAttribEXT; + + const char *extensionString = + static_cast(eglQueryString(mDisplay, EGL_EXTENSIONS)); + EXPECT_TRUE(strstr(extensionString, "EGL_EXT_device_query")); + + queryDisplayAttribEXT = + (PFNEGLQUERYDISPLAYATTRIBEXTPROC)eglGetProcAddress("eglQueryDisplayAttribEXT"); + queryDeviceAttribEXT = + (PFNEGLQUERYDEVICEATTRIBEXTPROC)eglGetProcAddress("eglQueryDeviceAttribEXT"); + ASSERT_NE(nullptr, queryDisplayAttribEXT); + ASSERT_NE(nullptr, queryDeviceAttribEXT); + + ASSERT_EGL_TRUE(queryDisplayAttribEXT(mDisplay, EGL_DEVICE_EXT, &angleDevice)); + ASSERT_EGL_TRUE(queryDeviceAttribEXT(reinterpret_cast(angleDevice), + EGL_D3D11_DEVICE_ANGLE, &device)); + ID3D11Device *d3d11Device = reinterpret_cast(device); + + D3D11_TEXTURE2D_DESC textureDesc = {0}; + textureDesc.Width = mWindowWidth; + textureDesc.Height = mWindowWidth; + textureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + textureDesc.MipLevels = 1; + textureDesc.ArraySize = 1; + textureDesc.SampleDesc.Count = 1; + textureDesc.SampleDesc.Quality = 0; + textureDesc.Usage = D3D11_USAGE_DEFAULT; + textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; + textureDesc.CPUAccessFlags = 0; + textureDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; + + ASSERT_TRUE(SUCCEEDED( + d3d11Device->CreateTexture2D(&textureDesc, nullptr, &mOffscreenSurfaceD3D11Texture))); + + IDXGIResource *dxgiResource = + DynamicCastComObject(mOffscreenSurfaceD3D11Texture); + ASSERT_NE(nullptr, dxgiResource); + + HANDLE sharedHandle = 0; + ASSERT_TRUE(SUCCEEDED(dxgiResource->GetSharedHandle(&sharedHandle))); + SafeRelease(dxgiResource); + + EGLint pBufferAttributes[] = {EGL_WIDTH, mWindowWidth, EGL_HEIGHT, + mWindowWidth, EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, + EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, EGL_NONE}; + + mSurface = eglCreatePbufferFromClientBuffer(mDisplay, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, + sharedHandle, mConfig, pBufferAttributes); + ASSERT_TRUE(EGL_NO_SURFACE != mSurface); + } + + void makeCurrent() { ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)); } + + void TearDown() override + { + if (mDisplay != EGL_NO_DISPLAY) + { + eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (mSurface != EGL_NO_SURFACE) + { + eglDestroySurface(mDisplay, mSurface); + mSurface = EGL_NO_SURFACE; + } + + if (mContext != EGL_NO_CONTEXT) + { + eglDestroyContext(mDisplay, mContext); + mContext = EGL_NO_CONTEXT; + } + + eglTerminate(mDisplay); + mDisplay = EGL_NO_DISPLAY; + } + + mOSWindow->destroy(); + SafeDelete(mOSWindow); + + SafeRelease(mOffscreenSurfaceD3D11Texture); + } + + void drawQuadUsingGL() + { + GLuint m2DProgram; + GLint mTexture2DUniformLocation; + + const std::string vertexShaderSource = + SHADER_SOURCE(precision highp float; attribute vec4 position; varying vec2 texcoord; + + void main() + { + gl_Position = vec4(position.xy, 0.0, 1.0); + texcoord = (position.xy * 0.5) + 0.5; + }); + + const std::string fragmentShaderSource2D = + SHADER_SOURCE(precision highp float; uniform sampler2D tex; varying vec2 texcoord; + + void main() + { + gl_FragColor = texture2D(tex, texcoord); + }); + + m2DProgram = CompileProgram(vertexShaderSource, fragmentShaderSource2D); + mTexture2DUniformLocation = glGetUniformLocation(m2DProgram, "tex"); + + uint8_t textureInitData[16] = { + 255, 0, 0, 255, // Red + 0, 255, 0, 255, // Green + 0, 0, 255, 255, // Blue + 255, 255, 0, 255 // Red + Green + }; + + // Create a simple RGBA texture + GLuint tex = 0; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, + textureInitData); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + ASSERT_GL_NO_ERROR(); + + // Draw a quad using the texture + glClear(GL_COLOR_BUFFER_BIT); + glUseProgram(m2DProgram); + glUniform1i(mTexture2DUniformLocation, 0); + + GLint positionLocation = glGetAttribLocation(m2DProgram, "position"); + glUseProgram(m2DProgram); + const GLfloat vertices[] = + { + -1.0f, 1.0f, 0.5f, + -1.0f, -1.0f, 0.5f, + 1.0f, -1.0f, 0.5f, + -1.0f, 1.0f, 0.5f, + 1.0f, -1.0f, 0.5f, + 1.0f, 1.0f, 0.5f, + }; + + glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices); + glEnableVertexAttribArray(positionLocation); + + glDrawArrays(GL_TRIANGLES, 0, 6); + ASSERT_GL_NO_ERROR(); + + glDeleteProgram(m2DProgram); + } + + void checkPixelsUsingGL() + { + // Note that the texture is in BGRA format + EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); // Red + EXPECT_PIXEL_EQ(mWindowWidth - 1, 0, 0, 255, 0, 255); // Green + EXPECT_PIXEL_EQ(0, mWindowWidth - 1, 0, 0, 255, 255); // Blue + EXPECT_PIXEL_EQ(mWindowWidth - 1, mWindowWidth - 1, 255, 255, 0, 255); // Red + green + } + + void checkPixelsUsingD3D(bool usingPresentPathFast) + { + ASSERT_NE(nullptr, mOffscreenSurfaceD3D11Texture); + + D3D11_TEXTURE2D_DESC textureDesc = {0}; + ID3D11Device *device; + ID3D11DeviceContext *context; + mOffscreenSurfaceD3D11Texture->GetDesc(&textureDesc); + mOffscreenSurfaceD3D11Texture->GetDevice(&device); + device->GetImmediateContext(&context); + ASSERT_NE(nullptr, device); + ASSERT_NE(nullptr, context); + + textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + textureDesc.Usage = D3D11_USAGE_STAGING; + textureDesc.BindFlags = 0; + textureDesc.MiscFlags = 0; + ID3D11Texture2D *cpuTexture = nullptr; + ASSERT_TRUE(SUCCEEDED(device->CreateTexture2D(&textureDesc, nullptr, &cpuTexture))); + + context->CopyResource(cpuTexture, mOffscreenSurfaceD3D11Texture); + + D3D11_MAPPED_SUBRESOURCE mappedSubresource; + context->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mappedSubresource); + ASSERT_EQ(static_cast(mWindowWidth * 4), mappedSubresource.RowPitch); + ASSERT_EQ(static_cast(mWindowWidth * mWindowWidth * 4), mappedSubresource.DepthPitch); + + angle::GLColor *byteData = reinterpret_cast(mappedSubresource.pData); + + // Note that the texture is in BGRA format, although the GLColor struct is RGBA + GLColor expectedTopLeftPixel = GLColor(0, 0, 255, 255); // Red + GLColor expectedTopRightPixel = GLColor(0, 255, 0, 255); // Green + GLColor expectedBottomLeftPixel = GLColor(255, 0, 0, 255); // Blue + GLColor expectedBottomRightPixel = GLColor(0, 255, 255, 255); // Red + Green + + if (usingPresentPathFast) + { + // Invert the expected values + GLColor tempTopLeft = expectedTopLeftPixel; + GLColor tempTopRight = expectedTopRightPixel; + expectedTopLeftPixel = expectedBottomLeftPixel; + expectedTopRightPixel = expectedBottomRightPixel; + expectedBottomLeftPixel = tempTopLeft; + expectedBottomRightPixel = tempTopRight; + } + + EXPECT_EQ(expectedTopLeftPixel, byteData[0]); + EXPECT_EQ(expectedTopRightPixel, byteData[(mWindowWidth - 1)]); + EXPECT_EQ(expectedBottomLeftPixel, byteData[(mWindowWidth - 1) * mWindowWidth]); + EXPECT_EQ(expectedBottomRightPixel, + byteData[(mWindowWidth - 1) * mWindowWidth + (mWindowWidth - 1)]); + + context->Unmap(cpuTexture, 0); + SafeRelease(cpuTexture); + SafeRelease(device); + SafeRelease(context); + } + + EGLDisplay mDisplay; + EGLContext mContext; + EGLSurface mSurface; + ID3D11Texture2D *mOffscreenSurfaceD3D11Texture; + EGLConfig mConfig; + OSWindow *mOSWindow; + GLint mWindowWidth; +}; + +// Test that rendering basic content onto a window surface when present path fast +// is enabled works as expected +TEST_P(EGLPresentPathD3D11, WindowPresentPathFast) +{ + initializeEGL(true); + createWindowSurface(); + makeCurrent(); + + drawQuadUsingGL(); + + checkPixelsUsingGL(); +} + +// Test that rendering basic content onto a client buffer surface when present path fast +// works as expected, and is also oriented the correct way around +TEST_P(EGLPresentPathD3D11, ClientBufferPresentPathFast) +{ + initializeEGL(true); + createPbufferFromClientBufferSurface(); + makeCurrent(); + + drawQuadUsingGL(); + + checkPixelsUsingGL(); + checkPixelsUsingD3D(true); +} + +// Test that rendering basic content onto a window surface when present path fast +// is disabled works as expected +TEST_P(EGLPresentPathD3D11, WindowPresentPathCopy) +{ + initializeEGL(false); + createWindowSurface(); + makeCurrent(); + + drawQuadUsingGL(); + + checkPixelsUsingGL(); +} + +// Test that rendering basic content onto a client buffer surface when present path +// fast is disabled works as expected, and is also oriented the correct way around +TEST_P(EGLPresentPathD3D11, ClientBufferPresentPathCopy) +{ + initializeEGL(false); + createPbufferFromClientBufferSurface(); + makeCurrent(); + + drawQuadUsingGL(); + + checkPixelsUsingGL(); + checkPixelsUsingD3D(false); +} + +ANGLE_INSTANTIATE_TEST(EGLPresentPathD3D11, ES2_D3D11(), ES2_D3D11_FL9_3()); \ No newline at end of file diff --git a/gfx/angle/src/tests/egl_tests/EGLSurfaceTest.cpp b/gfx/angle/src/tests/egl_tests/EGLSurfaceTest.cpp index cb299adb44..513b01d44e 100644 --- a/gfx/angle/src/tests/egl_tests/EGLSurfaceTest.cpp +++ b/gfx/angle/src/tests/egl_tests/EGLSurfaceTest.cpp @@ -97,7 +97,9 @@ class EGLSurfaceTest : public testing::Test } displayAttributes.push_back(EGL_NONE); - mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, mOSWindow->getNativeDisplay(), displayAttributes.data()); + mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, + reinterpret_cast(mOSWindow->getNativeDisplay()), + displayAttributes.data()); ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY); EGLint majorVersion, minorVersion; diff --git a/gfx/angle/src/tests/egl_tests/EGLX11VisualTest.cpp b/gfx/angle/src/tests/egl_tests/EGLX11VisualTest.cpp new file mode 100644 index 0000000000..01dc7ffbb5 --- /dev/null +++ b/gfx/angle/src/tests/egl_tests/EGLX11VisualTest.cpp @@ -0,0 +1,213 @@ +// +// Copyright (c) 2015 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. +// + +// EGLX11VisualTest.cpp: tests for EGL_ANGLE_x11_visual extension + +#include + +#include +#include +#include + +#include "OSWindow.h" +#include "test_utils/ANGLETest.h" +#include "x11/X11Window.h" + +using namespace angle; + +namespace +{ + +const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; +} + +class EGLX11VisualHintTest : public ::testing::TestWithParam +{ + public: + void SetUp() override + { + mEglGetPlatformDisplayEXT = reinterpret_cast( + eglGetProcAddress("eglGetPlatformDisplayEXT")); + + mDisplay = XOpenDisplay(NULL); + } + + std::vector getDisplayAttributes(int visualId) const + { + std::vector attribs; + + attribs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); + attribs.push_back(GetParam().getRenderer()); + attribs.push_back(EGL_X11_VISUAL_ID_ANGLE); + attribs.push_back(visualId); + attribs.push_back(EGL_NONE); + + return attribs; + } + + unsigned int chooseDifferentVisual(unsigned int visualId) + { + int numVisuals; + XVisualInfo visualTemplate; + visualTemplate.screen = DefaultScreen(mDisplay); + + XVisualInfo *visuals = + XGetVisualInfo(mDisplay, VisualScreenMask, &visualTemplate, &numVisuals); + EXPECT_TRUE(numVisuals >= 2); + + for (int i = 0; i < numVisuals; ++i) + { + if (visuals[i].visualid != visualId) + { + int result = visuals[i].visualid; + XFree(visuals); + return result; + } + } + + UNREACHABLE(); + return -1; + } + + protected: + PFNEGLGETPLATFORMDISPLAYEXTPROC mEglGetPlatformDisplayEXT; + Display *mDisplay; +}; + +// Test that display creation fails if the visual ID passed in invalid. +TEST_P(EGLX11VisualHintTest, InvalidVisualID) +{ + static const int gInvalidVisualId = -1; + auto attributes = getDisplayAttributes(gInvalidVisualId); + + EGLDisplay display = + mEglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, attributes.data()); + ASSERT_TRUE(display != EGL_NO_DISPLAY); + + ASSERT_TRUE(EGL_FALSE == eglInitialize(display, nullptr, nullptr)); + ASSERT_EGL_ERROR(EGL_NOT_INITIALIZED); +} + +// Test that context creation with a visual ID succeeds, that the context exposes +// only one config, and that a clear on a surface with this config works. +TEST_P(EGLX11VisualHintTest, ValidVisualIDAndClear) +{ + // We'll test the extension with one visual ID but we don't care which one. This means we + // can use OSWindow to create a window and just grab its visual. + OSWindow *osWindow = CreateOSWindow(); + osWindow->initialize("EGLX11VisualHintTest", 500, 500); + osWindow->setVisible(true); + + Window xWindow = osWindow->getNativeWindow(); + + XWindowAttributes windowAttributes; + ASSERT_NE(0, XGetWindowAttributes(mDisplay, xWindow, &windowAttributes)); + int visualId = windowAttributes.visual->visualid; + + auto attributes = getDisplayAttributes(visualId); + EGLDisplay display = + mEglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, attributes.data()); + ASSERT_NE(EGL_NO_DISPLAY, display); + + ASSERT_TRUE(EGL_TRUE == eglInitialize(display, nullptr, nullptr)); + + // While this is not required by the extension, test that our implementation returns only one + // config, with the same native visual Id that we provided. + int nConfigs = 0; + ASSERT_TRUE(EGL_TRUE == eglGetConfigs(display, nullptr, 0, &nConfigs)); + ASSERT_EQ(1, nConfigs); + + int nReturnedConfigs = 0; + EGLConfig config; + ASSERT_TRUE(EGL_TRUE == eglGetConfigs(display, &config, 1, &nReturnedConfigs)); + ASSERT_EQ(nConfigs, nReturnedConfigs); + + EGLint eglNativeId; + ASSERT_TRUE(EGL_TRUE == eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &eglNativeId)); + ASSERT_EQ(visualId, eglNativeId); + + // Finally, try to do a clear on the window. + EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs); + ASSERT_NE(EGL_NO_CONTEXT, context); + + EGLSurface window = eglCreateWindowSurface(display, config, xWindow, nullptr); + ASSERT_EGL_SUCCESS(); + + eglMakeCurrent(display, window, window, context); + ASSERT_EGL_SUCCESS(); + + glViewport(0, 0, 500, 500); + glClearColor(0.0f, 0.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + ASSERT_GL_NO_ERROR(); + EXPECT_PIXEL_EQ(250, 250, 0, 0, 255, 255); + + // Teardown + eglDestroySurface(display, window); + ASSERT_EGL_SUCCESS(); + + eglDestroyContext(display, context); + ASSERT_EGL_SUCCESS(); + + SafeDelete(osWindow); + + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(display); +} + +// Test that EGL_BAD_MATCH is generated when trying to create an EGL window from +// an X11 window whose visual ID doesn't match the visual ID passed at display creation. +TEST_P(EGLX11VisualHintTest, InvalidWindowVisualID) +{ + // Get the default visual ID, as a good guess of a visual id for which display + // creation will succeed. + int visualId; + { + OSWindow *osWindow = CreateOSWindow(); + osWindow->initialize("EGLX11VisualHintTest", 500, 500); + osWindow->setVisible(true); + + Window xWindow = osWindow->getNativeWindow(); + + XWindowAttributes windowAttributes; + ASSERT_NE(0, XGetWindowAttributes(mDisplay, xWindow, &windowAttributes)); + visualId = windowAttributes.visual->visualid; + + SafeDelete(osWindow); + } + + auto attributes = getDisplayAttributes(visualId); + EGLDisplay display = + mEglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, attributes.data()); + ASSERT_NE(EGL_NO_DISPLAY, display); + + ASSERT_TRUE(EGL_TRUE == eglInitialize(display, nullptr, nullptr)); + + + // Initialize the window with a visual id different from the display's visual id + int otherVisualId = chooseDifferentVisual(visualId); + ASSERT_NE(visualId, otherVisualId); + + OSWindow *osWindow = new X11Window(otherVisualId); + osWindow->initialize("EGLX11VisualHintTest", 500, 500); + osWindow->setVisible(true); + + Window xWindow = osWindow->getNativeWindow(); + + // Creating the EGL window should fail with EGL_BAD_MATCH + int nReturnedConfigs = 0; + EGLConfig config; + ASSERT_TRUE(EGL_TRUE == eglGetConfigs(display, &config, 1, &nReturnedConfigs)); + ASSERT_EQ(1, nReturnedConfigs); + + EGLSurface window = eglCreateWindowSurface(display, config, xWindow, nullptr); + ASSERT_EQ(EGL_NO_SURFACE, window); + ASSERT_EGL_ERROR(EGL_BAD_MATCH); + + SafeDelete(osWindow); +} + +ANGLE_INSTANTIATE_TEST(EGLX11VisualHintTest, ES2_OPENGL()); diff --git a/gfx/angle/src/tests/gl_tests/BlendMinMaxTest.cpp b/gfx/angle/src/tests/gl_tests/BlendMinMaxTest.cpp index 0b8bc818a8..d2f9036fd3 100644 --- a/gfx/angle/src/tests/gl_tests/BlendMinMaxTest.cpp +++ b/gfx/angle/src/tests/gl_tests/BlendMinMaxTest.cpp @@ -166,9 +166,10 @@ TEST_P(BlendMinMaxTest, RGBA8) TEST_P(BlendMinMaxTest, RGBA32f) { - if (getClientVersion() < 3 && !extensionEnabled("GL_OES_texture_float")) + if (getClientVersion() < 3 || !extensionEnabled("GL_EXT_color_buffer_float")) { - std::cout << "Test skipped because ES3 or GL_OES_texture_float is not available." << std::endl; + std::cout << "Test skipped because ES3 and GL_EXT_color_buffer_float are not available." + << std::endl; return; } @@ -179,14 +180,22 @@ TEST_P(BlendMinMaxTest, RGBA32f) return; } + // TODO (bug 1284): Investigate RGBA32f D3D SDK Layers messages on D3D11_FL9_3 + if (isD3D11_FL93()) + { + std::cout << "Test skipped on Feature Level 9_3." << std::endl; + return; + } + runTest(GL_RGBA32F); } TEST_P(BlendMinMaxTest, RGBA16F) { - if (getClientVersion() < 3 && !extensionEnabled("GL_OES_texture_half_float")) + if (getClientVersion() < 3 && !extensionEnabled("GL_EXT_color_buffer_half_float")) { - std::cout << "Test skipped because ES3 or GL_OES_texture_half_float is not available." << std::endl; + std::cout << "Test skipped because ES3 or GL_EXT_color_buffer_half_float is not available." + << std::endl; return; } @@ -197,8 +206,21 @@ TEST_P(BlendMinMaxTest, RGBA16F) return; } + // TODO(geofflang): This fails because readpixels with UNSIGNED_BYTE/RGBA does not work with + // half float buffers (http://anglebug.com/1288) + if (GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE) + { + std::cout << "Test skipped on OpenGL ES targets." << std::endl; + return; + } + runTest(GL_RGBA16F); } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(BlendMinMaxTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL()); +ANGLE_INSTANTIATE_TEST(BlendMinMaxTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES2_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/BlitFramebufferANGLETest.cpp b/gfx/angle/src/tests/gl_tests/BlitFramebufferANGLETest.cpp index 676b1043ae..2184086cec 100644 --- a/gfx/angle/src/tests/gl_tests/BlitFramebufferANGLETest.cpp +++ b/gfx/angle/src/tests/gl_tests/BlitFramebufferANGLETest.cpp @@ -888,5 +888,11 @@ TEST_P(BlitFramebufferANGLETest, Errors) } +// TODO(geofflang): Fix the dependence on glBlitFramebufferANGLE without checks and assuming the +// default framebuffer is BGRA to enable the GL and GLES backends. (http://anglebug.com/1289) + // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(BlitFramebufferANGLETest, ES2_D3D9(), ES2_D3D11()); +ANGLE_INSTANTIATE_TEST(BlitFramebufferANGLETest, + ES2_D3D9(), + ES2_D3D11(EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE), + ES2_D3D11(EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE)); diff --git a/gfx/angle/src/tests/gl_tests/BufferDataTest.cpp b/gfx/angle/src/tests/gl_tests/BufferDataTest.cpp index b5d9231bd0..3984d9e9c3 100644 --- a/gfx/angle/src/tests/gl_tests/BufferDataTest.cpp +++ b/gfx/angle/src/tests/gl_tests/BufferDataTest.cpp @@ -287,6 +287,13 @@ class IndexedBufferCopyTest : public ANGLETest // https://code.google.com/p/angleproject/issues/detail?id=709 TEST_P(IndexedBufferCopyTest, IndexRangeBug) { + // TODO(geofflang): Figure out why this fails on AMD OpenGL (http://anglebug.com/1291) + if (isAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test disabled on AMD OpenGL." << std::endl; + return; + } + unsigned char vertexData[] = { 255, 0, 0, 0, 0, 0 }; unsigned int indexData[] = { 0, 1 }; @@ -401,9 +408,9 @@ TEST_P(BufferDataTestES3, BufferResizing) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(BufferDataTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL()); -ANGLE_INSTANTIATE_TEST(BufferDataTestES3, ES3_D3D11()); -ANGLE_INSTANTIATE_TEST(IndexedBufferCopyTest, ES3_D3D11()); +ANGLE_INSTANTIATE_TEST(BufferDataTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES()); +ANGLE_INSTANTIATE_TEST(BufferDataTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); +ANGLE_INSTANTIATE_TEST(IndexedBufferCopyTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); #ifdef _WIN64 diff --git a/gfx/angle/src/tests/gl_tests/ClearTest.cpp b/gfx/angle/src/tests/gl_tests/ClearTest.cpp index 5062bdc4a0..1517a5227b 100644 --- a/gfx/angle/src/tests/gl_tests/ClearTest.cpp +++ b/gfx/angle/src/tests/gl_tests/ClearTest.cpp @@ -6,12 +6,37 @@ #include "test_utils/ANGLETest.h" +#include "random_utils.h" +#include "Vector.h" + using namespace angle; +namespace +{ + +Vector4 RandomVec4(int seed, float minValue, float maxValue) +{ + RNG rng(seed); + srand(seed); + return Vector4( + rng.randomFloatBetween(minValue, maxValue), rng.randomFloatBetween(minValue, maxValue), + rng.randomFloatBetween(minValue, maxValue), rng.randomFloatBetween(minValue, maxValue)); +} + +GLColor Vec4ToColor(const Vector4 &vec) +{ + GLColor color; + color.R = static_cast(vec.x * 255.0f); + color.G = static_cast(vec.y * 255.0f); + color.B = static_cast(vec.z * 255.0f); + color.A = static_cast(vec.w * 255.0f); + return color; +}; + class ClearTestBase : public ANGLETest { protected: - ClearTestBase() + ClearTestBase() : mProgram(0) { setWindowWidth(128); setWindowHeight(128); @@ -22,10 +47,35 @@ class ClearTestBase : public ANGLETest setConfigDepthBits(24); } - virtual void SetUp() + void SetUp() override { ANGLETest::SetUp(); + mFBOs.resize(2, 0); + glGenFramebuffers(2, mFBOs.data()); + + ASSERT_GL_NO_ERROR(); + } + + void TearDown() override + { + glDeleteProgram(mProgram); + + if (!mFBOs.empty()) + { + glDeleteFramebuffers(static_cast(mFBOs.size()), mFBOs.data()); + } + + if (!mTextures.empty()) + { + glDeleteTextures(static_cast(mTextures.size()), mTextures.data()); + } + + ANGLETest::TearDown(); + } + + void setupDefaultProgram() + { const std::string vertexShaderSource = SHADER_SOURCE ( precision highp float; @@ -48,26 +98,12 @@ class ClearTestBase : public ANGLETest ); mProgram = CompileProgram(vertexShaderSource, fragmentShaderSource); - if (mProgram == 0) - { - FAIL() << "shader compilation failed."; - } - - glGenFramebuffers(1, &mFBO); - - ASSERT_GL_NO_ERROR(); - } - - virtual void TearDown() - { - glDeleteProgram(mProgram); - glDeleteFramebuffers(1, &mFBO); - - ANGLETest::TearDown(); + ASSERT_NE(0u, mProgram); } GLuint mProgram; - GLuint mFBO; + std::vector mFBOs; + std::vector mTextures; }; class ClearTest : public ClearTestBase {}; @@ -84,7 +120,7 @@ TEST_P(ClearTest, DefaultFramebuffer) // Test clearing a RGBA8 Framebuffer TEST_P(ClearTest, RGBA8Framebuffer) { - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mFBOs[0]); GLuint texture; glGenTextures(1, &texture); @@ -118,7 +154,7 @@ TEST_P(ClearTest, ClearIssue) EXPECT_GL_NO_ERROR(); - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mFBOs[0]); GLuint rbo; glGenRenderbuffers(1, &rbo); @@ -140,6 +176,7 @@ TEST_P(ClearTest, ClearIssue) glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); + setupDefaultProgram(); drawQuad(mProgram, "position", 0.5f); EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); @@ -152,7 +189,7 @@ TEST_P(ClearTestES3, MaskedClearBufferBug) { unsigned char pixelData[] = { 255, 255, 255, 255 }; - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mFBOs[0]); GLuint textures[2]; glGenTextures(2, &textures[0]); @@ -177,11 +214,9 @@ TEST_P(ClearTestES3, MaskedClearBufferBug) ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_EQ(0, 0, 255, 255, 255, 255); - // TODO: glReadBuffer support - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1], 0); + glReadBuffer(GL_COLOR_ATTACHMENT1); + ASSERT_GL_NO_ERROR(); - //TODO(jmadill): Robust handling of pixel test error ranges EXPECT_PIXEL_NEAR(0, 0, 0, 127, 255, 255, 1); glDeleteTextures(2, textures); @@ -190,7 +225,7 @@ TEST_P(ClearTestES3, MaskedClearBufferBug) TEST_P(ClearTestES3, BadFBOSerialBug) { // First make a simple framebuffer, and clear it to green - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mFBOs[0]); GLuint textures[2]; glGenTextures(2, &textures[0]); @@ -222,13 +257,14 @@ TEST_P(ClearTestES3, BadFBOSerialBug) glDrawBuffers(1, drawBuffers); + setupDefaultProgram(); drawQuad(mProgram, "position", 0.5f); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); // Check that the first framebuffer is still green. - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mFBOs[0]); EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); glDeleteTextures(2, textures); @@ -246,7 +282,7 @@ TEST_P(ClearTestES3, SRGBClear) } // First make a simple framebuffer, and clear it - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mFBOs[0]); GLuint texture; glGenTextures(1, &texture); @@ -281,7 +317,7 @@ TEST_P(ClearTestES3, MixedSRGBClear) return; } - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mFBOs[0]); GLuint textures[2]; glGenTextures(2, &textures[0]); @@ -313,6 +349,133 @@ TEST_P(ClearTestES3, MixedSRGBClear) EXPECT_PIXEL_NEAR(0, 0, 128, 128, 128, 128, 1.0); } +// This test covers a D3D11 bug where calling ClearRenderTargetView sometimes wouldn't sync +// before a draw call. The test draws small quads to a larger FBO (the default back buffer). +// Before each blit to the back buffer it clears the quad to a certain color using +// ClearBufferfv to give a solid color. The sync problem goes away if we insert a call to +// flush or finish after ClearBufferfv or each draw. +TEST_P(ClearTestES3, RepeatedClear) +{ + if (isD3D11() && (isNVidia() || isIntel())) + { + std::cout << "Test skipped on Nvidia and Intel D3D11." << std::endl; + return; + } + + const std::string &vertexSource = + "#version 300 es\n" + "in highp vec2 position;\n" + "out highp vec2 v_coord;\n" + "void main(void)\n" + "{\n" + " gl_Position = vec4(position, 0, 1);\n" + " vec2 texCoord = (position * 0.5) + 0.5;\n" + " v_coord = texCoord;\n" + "}\n"; + + const std::string &fragmentSource = + "#version 300 es\n" + "in highp vec2 v_coord;\n" + "out highp vec4 color;\n" + "uniform sampler2D tex;\n" + "void main()\n" + "{\n" + " color = texture(tex, v_coord);\n" + "}\n"; + + mProgram = CompileProgram(vertexSource, fragmentSource); + ASSERT_NE(0u, mProgram); + + mTextures.resize(1, 0); + glGenTextures(1, mTextures.data()); + + GLenum format = GL_RGBA8; + const int numRowsCols = 3; + const int cellSize = 32; + const int fboSize = cellSize; + const int backFBOSize = cellSize * numRowsCols; + const float fmtValueMin = 0.0f; + const float fmtValueMax = 1.0f; + + glBindTexture(GL_TEXTURE_2D, mTextures[0]); + glTexStorage2D(GL_TEXTURE_2D, 1, format, fboSize, fboSize); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + ASSERT_GL_NO_ERROR(); + + glBindFramebuffer(GL_FRAMEBUFFER, mFBOs[0]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); + ASSERT_GL_NO_ERROR(); + + ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); + + // larger fbo bound -- clear to transparent black + glUseProgram(mProgram); + GLint uniLoc = glGetUniformLocation(mProgram, "tex"); + ASSERT_NE(-1, uniLoc); + glUniform1i(uniLoc, 0); + glBindTexture(GL_TEXTURE_2D, mTextures[0]); + + GLint positionLocation = glGetAttribLocation(mProgram, "position"); + ASSERT_NE(-1, positionLocation); + + glUseProgram(mProgram); + + for (int cellY = 0; cellY < numRowsCols; cellY++) + { + for (int cellX = 0; cellX < numRowsCols; cellX++) + { + int seed = cellX + cellY * numRowsCols; + const Vector4 color = RandomVec4(seed, fmtValueMin, fmtValueMax); + + glBindFramebuffer(GL_FRAMEBUFFER, mFBOs[0]); + glClearBufferfv(GL_COLOR, 0, color.data()); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Method 1: Set viewport and draw full-viewport quad + glViewport(cellX * cellSize, cellY * cellSize, cellSize, cellSize); + drawQuad(mProgram, "position", 0.5f); + + // Uncommenting the glFinish call seems to make the test pass. + // glFinish(); + } + } + + std::vector pixelData(backFBOSize * backFBOSize); + glReadPixels(0, 0, backFBOSize, backFBOSize, GL_RGBA, GL_UNSIGNED_BYTE, pixelData.data()); + + for (int cellY = 0; cellY < numRowsCols; cellY++) + { + for (int cellX = 0; cellX < numRowsCols; cellX++) + { + int seed = cellX + cellY * numRowsCols; + const Vector4 color = RandomVec4(seed, fmtValueMin, fmtValueMax); + GLColor expectedColor = Vec4ToColor(color); + + int testN = cellX * cellSize + cellY * backFBOSize * cellSize + backFBOSize + 1; + GLColor actualColor = pixelData[testN]; + EXPECT_NEAR(expectedColor.R, actualColor.R, 1); + EXPECT_NEAR(expectedColor.G, actualColor.G, 1); + EXPECT_NEAR(expectedColor.B, actualColor.B, 1); + EXPECT_NEAR(expectedColor.A, actualColor.A, 1); + } + } + + ASSERT_GL_NO_ERROR(); +} + // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(ClearTest, ES2_D3D9(), ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), ES3_OPENGL()); -ANGLE_INSTANTIATE_TEST(ClearTestES3, ES3_D3D11(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(ClearTest, + ES2_D3D9(), + ES2_D3D11(), + ES3_D3D11(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); +ANGLE_INSTANTIATE_TEST(ClearTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); + +} // anonymous namespace diff --git a/gfx/angle/src/tests/gl_tests/CompressedTextureTest.cpp b/gfx/angle/src/tests/gl_tests/CompressedTextureTest.cpp index 753eac3f03..5b1fb4f82c 100644 --- a/gfx/angle/src/tests/gl_tests/CompressedTextureTest.cpp +++ b/gfx/angle/src/tests/gl_tests/CompressedTextureTest.cpp @@ -302,11 +302,16 @@ TEST_P(CompressedTextureTestD3D11, PBOCompressedTexStorage) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST( - CompressedTextureTest, - ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3(), ES2_OPENGL(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(CompressedTextureTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(CompressedTextureTestES3, ES3_D3D11()); +ANGLE_INSTANTIATE_TEST(CompressedTextureTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); ANGLE_INSTANTIATE_TEST(CompressedTextureTestD3D11, ES2_D3D11(), ES3_D3D11(), ES2_D3D11_FL9_3()); diff --git a/gfx/angle/src/tests/gl_tests/CopyTexImageTest.cpp b/gfx/angle/src/tests/gl_tests/CopyTexImageTest.cpp index 6c891e8715..00f14ba181 100644 --- a/gfx/angle/src/tests/gl_tests/CopyTexImageTest.cpp +++ b/gfx/angle/src/tests/gl_tests/CopyTexImageTest.cpp @@ -300,5 +300,11 @@ TEST_P(CopyTexImageTest, SubImageRGBToL) // Use this to select which configurations (e.g. which renderer, which GLES major version) these // tests should be run against. -ANGLE_INSTANTIATE_TEST(CopyTexImageTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_OPENGL(3, 3)); +ANGLE_INSTANTIATE_TEST(CopyTexImageTest, + ES2_D3D9(), + ES2_D3D11(EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE), + ES2_D3D11(EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE), + ES2_OPENGL(), + ES2_OPENGL(3, 3), + ES2_OPENGLES()); } diff --git a/gfx/angle/src/tests/gl_tests/CubeMapTextureTest.cpp b/gfx/angle/src/tests/gl_tests/CubeMapTextureTest.cpp index 7d99f7c6ea..10fce15b05 100644 --- a/gfx/angle/src/tests/gl_tests/CubeMapTextureTest.cpp +++ b/gfx/angle/src/tests/gl_tests/CubeMapTextureTest.cpp @@ -129,4 +129,10 @@ TEST_P(CubeMapTextureTest, RenderToFacesConsecutively) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(CubeMapTextureTest, ES2_D3D11(), ES2_D3D11_FL9_3(), ES2_OPENGL(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(CubeMapTextureTest, + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/DebugMarkerTest.cpp b/gfx/angle/src/tests/gl_tests/DebugMarkerTest.cpp index 344cb14f8e..6bd4a43227 100644 --- a/gfx/angle/src/tests/gl_tests/DebugMarkerTest.cpp +++ b/gfx/angle/src/tests/gl_tests/DebugMarkerTest.cpp @@ -66,6 +66,6 @@ TEST_P(DebugMarkerTest, BasicValidation) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(DebugMarkerTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL()); +ANGLE_INSTANTIATE_TEST(DebugMarkerTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES()); } // namespace diff --git a/gfx/angle/src/tests/gl_tests/DebugTest.cpp b/gfx/angle/src/tests/gl_tests/DebugTest.cpp new file mode 100644 index 0000000000..46af9941f5 --- /dev/null +++ b/gfx/angle/src/tests/gl_tests/DebugTest.cpp @@ -0,0 +1,451 @@ +// +// Copyright 2015 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. +// + +// DebugTest.cpp : Tests of the GL_KHR_debug extension + +#include "test_utils/ANGLETest.h" + +namespace angle +{ + +class DebugTest : public ANGLETest +{ + protected: + DebugTest() : mDebugExtensionAvailable(false) + { + setWindowWidth(128); + setWindowHeight(128); + setConfigRedBits(8); + setConfigGreenBits(8); + setConfigBlueBits(8); + setConfigAlphaBits(8); + setConfigDepthBits(24); + setDebugEnabled(true); + } + + void SetUp() override + { + ANGLETest::SetUp(); + + mDebugExtensionAvailable = extensionEnabled("GL_KHR_debug"); + if (mDebugExtensionAvailable) + { + glEnable(GL_DEBUG_OUTPUT); + } + } + + bool mDebugExtensionAvailable; +}; + +struct Message +{ + GLenum source; + GLenum type; + GLuint id; + GLenum severity; + std::string message; + const void *userParam; +}; + +static void GL_APIENTRY Callback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar *message, + const void *userParam) +{ + Message m{source, type, id, severity, std::string(message, length), userParam}; + std::vector *messages = + static_cast *>(const_cast(userParam)); + messages->push_back(m); +} + +// Test that all ANGLE back-ends have GL_KHR_debug enabled +TEST_P(DebugTest, Enabled) +{ + ASSERT_TRUE(mDebugExtensionAvailable); +} + +// Test that when debug output is disabled, no message are outputted +TEST_P(DebugTest, DisabledOutput) +{ + if (!mDebugExtensionAvailable) + { + std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; + return; + } + + glDisable(GL_DEBUG_OUTPUT); + + glDebugMessageInsertKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, 1, + GL_DEBUG_SEVERITY_NOTIFICATION, -1, "discarded"); + + GLint numMessages = 0; + glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); + ASSERT_EQ(0, numMessages); + + std::vector messages; + glDebugMessageCallbackKHR(Callback, &messages); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + + ASSERT_EQ(0u, messages.size()); +} + +// Test a basic flow of inserting a message and reading it back +TEST_P(DebugTest, InsertMessage) +{ + if (!mDebugExtensionAvailable) + { + std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; + return; + } + + const GLenum source = GL_DEBUG_SOURCE_APPLICATION; + const GLenum type = GL_DEBUG_TYPE_OTHER; + const GLuint id = 1; + const GLenum severity = GL_DEBUG_SEVERITY_NOTIFICATION; + const std::string message = "Message"; + + glDebugMessageInsertKHR(source, type, id, severity, -1, message.c_str()); + + GLint numMessages = 0; + glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); + ASSERT_EQ(1, numMessages); + + GLint messageLength = 0; + glGetIntegerv(GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH, &messageLength); + EXPECT_EQ(static_cast(message.length()), messageLength); + + GLenum sourceBuf = 0; + GLenum typeBuf = 0; + GLenum idBuf = 0; + GLenum severityBuf = 0; + GLsizei lengthBuf = 0; + std::vector messageBuf(messageLength + 1); + GLuint ret = + glGetDebugMessageLogKHR(1, static_cast(messageBuf.size()), &sourceBuf, &typeBuf, + &idBuf, &severityBuf, &lengthBuf, messageBuf.data()); + EXPECT_EQ(1u, ret); + EXPECT_EQ(source, sourceBuf); + EXPECT_EQ(type, typeBuf); + EXPECT_EQ(id, idBuf); + EXPECT_EQ(severity, severityBuf); + EXPECT_EQ(lengthBuf, messageLength); + EXPECT_STREQ(message.c_str(), messageBuf.data()); + + glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); + EXPECT_EQ(0, numMessages); + + ASSERT_GL_NO_ERROR(); +} + +// Test inserting multiple messages +TEST_P(DebugTest, InsertMessageMultiple) +{ + if (!mDebugExtensionAvailable) + { + std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; + return; + } + + const GLenum source = GL_DEBUG_SOURCE_APPLICATION; + const GLenum type = GL_DEBUG_TYPE_OTHER; + const GLuint startID = 1; + const GLenum severity = GL_DEBUG_SEVERITY_NOTIFICATION; + const char messageRepeatChar = 'm'; + const size_t messageCount = 32; + + for (size_t i = 0; i < messageCount; i++) + { + std::string message(i + 1, messageRepeatChar); + glDebugMessageInsertKHR(source, type, startID + static_cast(i), severity, -1, + message.c_str()); + } + + GLint numMessages = 0; + glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); + ASSERT_EQ(static_cast(messageCount), numMessages); + + for (size_t i = 0; i < messageCount; i++) + { + glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); + EXPECT_EQ(static_cast(messageCount - i), numMessages); + + std::string expectedMessage(i + 1, messageRepeatChar); + + GLint messageLength = 0; + glGetIntegerv(GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH, &messageLength); + EXPECT_EQ(static_cast(expectedMessage.length()), messageLength); + + GLenum sourceBuf = 0; + GLenum typeBuf = 0; + GLenum idBuf = 0; + GLenum severityBuf = 0; + GLsizei lengthBuf = 0; + std::vector messageBuf(messageLength + 1); + GLuint ret = + glGetDebugMessageLogKHR(1, static_cast(messageBuf.size()), &sourceBuf, + &typeBuf, &idBuf, &severityBuf, &lengthBuf, messageBuf.data()); + EXPECT_EQ(1u, ret); + EXPECT_EQ(source, sourceBuf); + EXPECT_EQ(type, typeBuf); + EXPECT_EQ(startID + i, idBuf); + EXPECT_EQ(severity, severityBuf); + EXPECT_EQ(lengthBuf, messageLength); + EXPECT_STREQ(expectedMessage.c_str(), messageBuf.data()); + } + + glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); + EXPECT_EQ(0, numMessages); + + ASSERT_GL_NO_ERROR(); +} + +// Test using a debug callback +TEST_P(DebugTest, DebugCallback) +{ + if (!mDebugExtensionAvailable) + { + std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; + return; + } + + std::vector messages; + + glDebugMessageCallbackKHR(Callback, &messages); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + + const GLenum source = GL_DEBUG_SOURCE_APPLICATION; + const GLenum type = GL_DEBUG_TYPE_OTHER; + const GLuint id = 1; + const GLenum severity = GL_DEBUG_SEVERITY_NOTIFICATION; + const std::string message = "Message"; + + glDebugMessageInsertKHR(source, type, id, severity, -1, message.c_str()); + + GLint numMessages = 0; + glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); + EXPECT_EQ(0, numMessages); + + ASSERT_EQ(1u, messages.size()); + + const Message &m = messages.front(); + EXPECT_EQ(source, m.source); + EXPECT_EQ(type, m.type); + EXPECT_EQ(id, m.id); + EXPECT_EQ(severity, m.severity); + EXPECT_EQ(message, m.message); + + ASSERT_GL_NO_ERROR(); +} + +// Test the glGetPointervKHR entry point +TEST_P(DebugTest, GetPointer) +{ + if (!mDebugExtensionAvailable) + { + std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; + return; + } + + std::vector messages; + + glDebugMessageCallbackKHR(Callback, &messages); + + void *callback = nullptr; + glGetPointervKHR(GL_DEBUG_CALLBACK_FUNCTION, &callback); + EXPECT_EQ(reinterpret_cast(Callback), callback); + + void *userData = nullptr; + glGetPointervKHR(GL_DEBUG_CALLBACK_USER_PARAM, &userData); + EXPECT_EQ(static_cast(&messages), userData); +} + +// Test usage of message control. Example taken from GL_KHR_debug spec. +TEST_P(DebugTest, MessageControl1) +{ + if (!mDebugExtensionAvailable) + { + std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; + return; + } + + std::vector messages; + + glDebugMessageCallbackKHR(Callback, &messages); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + + // Setup of the default active debug group: Filter everything in + glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); + + // Generate a debug marker debug output message + glDebugMessageInsertKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 100, + GL_DEBUG_SEVERITY_NOTIFICATION, -1, "Message 1"); + + // Push debug group 1 + glPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION, 1, -1, "Message 2"); + + // Setup of the debug group 1: Filter everything out + glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_FALSE); + + // This message won't appear in the debug output log of + glDebugMessageInsertKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 100, + GL_DEBUG_SEVERITY_NOTIFICATION, -1, "Message 3"); + + // Pop debug group 1, restore the volume control of the default debug group. + glPopDebugGroupKHR(); + + // Generate a debug marker debug output message + glDebugMessageInsertKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 100, + GL_DEBUG_SEVERITY_NOTIFICATION, -1, "Message 5"); + + // Expected debug output from the GL implementation + // Message 1 + // Message 2 + // Message 2 + // Message 5 + EXPECT_EQ(4u, messages.size()); + EXPECT_STREQ(messages[0].message.c_str(), "Message 1"); + EXPECT_STREQ(messages[1].message.c_str(), "Message 2"); + EXPECT_STREQ(messages[2].message.c_str(), "Message 2"); + EXPECT_STREQ(messages[3].message.c_str(), "Message 5"); + + ASSERT_GL_NO_ERROR(); +} + +// Test usage of message control. Example taken from GL_KHR_debug spec. +TEST_P(DebugTest, MessageControl2) +{ + if (!mDebugExtensionAvailable) + { + std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; + return; + } + + std::vector messages; + + glDebugMessageCallbackKHR(Callback, &messages); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + + // Setup the control of de debug output for the default debug group + glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_FALSE); + glDebugMessageControlKHR(GL_DEBUG_SOURCE_THIRD_PARTY, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, + GL_FALSE); + std::vector ids0 = {1234, 2345, 3456, 4567}; + glDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, GL_DONT_CARE, + static_cast(ids0.size()), ids0.data(), GL_FALSE); + glDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, GL_DONT_CARE, + static_cast(ids0.size()), ids0.data(), GL_FALSE); + + // Push debug group 1 + // Inherit of the default debug group debug output volume control + // Filtered out by glDebugMessageControl + glPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION, 1, -1, "Message 1"); + + // In this section of the code, we are interested in performances. + glDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PERFORMANCE, GL_DONT_CARE, + 0, NULL, GL_TRUE); + // But we already identify that some messages are not really useful for us. + std::vector ids1 = {5678, 6789}; + glDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, GL_DONT_CARE, + static_cast(ids1.size()), ids1.data(), GL_FALSE); + + glDebugMessageInsertKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PERFORMANCE, 1357, + GL_DEBUG_SEVERITY_MEDIUM, -1, "Message 2"); + glDebugMessageInsertKHR(GL_DEBUG_SOURCE_THIRD_PARTY, // We still filter out these messages. + GL_DEBUG_TYPE_OTHER, 3579, GL_DEBUG_SEVERITY_MEDIUM, -1, "Message 3"); + + glPopDebugGroupKHR(); + + // Expected debug output from the GL implementation + // Message 2 + EXPECT_EQ(1u, messages.size()); + EXPECT_STREQ(messages[0].message.c_str(), "Message 2"); + + ASSERT_GL_NO_ERROR(); +} + +// Test basic usage of setting and getting labels +TEST_P(DebugTest, ObjectLabels) +{ + if (!mDebugExtensionAvailable) + { + std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; + return; + } + + GLuint renderbuffer = 0; + glGenRenderbuffers(1, &renderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); + + const std::string &label = "renderbuffer"; + glObjectLabelKHR(GL_RENDERBUFFER, renderbuffer, -1, label.c_str()); + + std::vector labelBuf(label.length() + 1); + GLsizei labelLengthBuf = 0; + glGetObjectLabelKHR(GL_RENDERBUFFER, renderbuffer, static_cast(labelBuf.size()), + &labelLengthBuf, labelBuf.data()); + + EXPECT_EQ(static_cast(label.length()), labelLengthBuf); + EXPECT_STREQ(label.c_str(), labelBuf.data()); + + ASSERT_GL_NO_ERROR(); + + glDeleteRenderbuffers(1, &renderbuffer); + + glObjectLabelKHR(GL_RENDERBUFFER, renderbuffer, -1, label.c_str()); + EXPECT_GL_ERROR(GL_INVALID_VALUE); + + glGetObjectLabelKHR(GL_RENDERBUFFER, renderbuffer, static_cast(labelBuf.size()), + &labelLengthBuf, labelBuf.data()); + EXPECT_GL_ERROR(GL_INVALID_VALUE); +} + +// Test basic usage of setting and getting labels +TEST_P(DebugTest, ObjectPtrLabels) +{ + if (!mDebugExtensionAvailable || getClientVersion() < 3) + { + std::cout << "Test skipped because GL_KHR_debug or ES3 is not available." << std::endl; + return; + } + + GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + + const std::string &label = "sync"; + glObjectPtrLabelKHR(sync, -1, label.c_str()); + + std::vector labelBuf(label.length() + 1); + GLsizei labelLengthBuf = 0; + glGetObjectPtrLabelKHR(sync, static_cast(labelBuf.size()), &labelLengthBuf, + labelBuf.data()); + + EXPECT_EQ(static_cast(label.length()), labelLengthBuf); + EXPECT_STREQ(label.c_str(), labelBuf.data()); + + ASSERT_GL_NO_ERROR(); + + glDeleteSync(sync); + + glObjectPtrLabelKHR(sync, -1, label.c_str()); + EXPECT_GL_ERROR(GL_INVALID_VALUE); + + glGetObjectPtrLabelKHR(sync, static_cast(labelBuf.size()), &labelLengthBuf, + labelBuf.data()); + EXPECT_GL_ERROR(GL_INVALID_VALUE); +} + +// Use this to select which configurations (e.g. which renderer, which GLES major version) these +// tests should be run against. +ANGLE_INSTANTIATE_TEST(DebugTest, + ES2_D3D9(), + ES2_D3D11(), + ES3_D3D11(), + ES2_OPENGL(), + ES3_OPENGL()); + +} // namespace angle diff --git a/gfx/angle/src/tests/gl_tests/DepthStencilFormatsTest.cpp b/gfx/angle/src/tests/gl_tests/DepthStencilFormatsTest.cpp index 3e44ae1754..f62a165784 100644 --- a/gfx/angle/src/tests/gl_tests/DepthStencilFormatsTest.cpp +++ b/gfx/angle/src/tests/gl_tests/DepthStencilFormatsTest.cpp @@ -184,5 +184,9 @@ TEST_P(DepthStencilFormatsTestES3, DrawWithDepthStencil) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(DepthStencilFormatsTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL()); -ANGLE_INSTANTIATE_TEST(DepthStencilFormatsTestES3, ES3_D3D11(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(DepthStencilFormatsTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_OPENGL(), + ES2_OPENGLES()); +ANGLE_INSTANTIATE_TEST(DepthStencilFormatsTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/DiscardFramebufferEXTTest.cpp b/gfx/angle/src/tests/gl_tests/DiscardFramebufferEXTTest.cpp index 97b3d76bc4..1e09ed120f 100644 --- a/gfx/angle/src/tests/gl_tests/DiscardFramebufferEXTTest.cpp +++ b/gfx/angle/src/tests/gl_tests/DiscardFramebufferEXTTest.cpp @@ -133,4 +133,11 @@ TEST_P(DiscardFramebufferEXTTest, NonDefaultFramebuffer) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(DiscardFramebufferEXTTest, ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3(), ES2_OPENGL(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(DiscardFramebufferEXTTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/DrawBuffersTest.cpp b/gfx/angle/src/tests/gl_tests/DrawBuffersTest.cpp index b9d3cb86ba..49c5282218 100644 --- a/gfx/angle/src/tests/gl_tests/DrawBuffersTest.cpp +++ b/gfx/angle/src/tests/gl_tests/DrawBuffersTest.cpp @@ -38,7 +38,8 @@ class DrawBuffersTest : public ANGLETest for (size_t texIndex = 0; texIndex < ArraySize(mTextures); texIndex++) { glBindTexture(GL_TEXTURE_2D, mTextures[texIndex]); - glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, getWindowWidth(), getWindowHeight()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); } GLfloat data[] = @@ -231,6 +232,13 @@ TEST_P(DrawBuffersTest, VerifyD3DLimits) TEST_P(DrawBuffersTest, Gaps) { + if (getClientVersion() < 3 && !extensionEnabled("GL_EXT_draw_buffers")) + { + std::cout << "Test skipped because ES3 or GL_EXT_draw_buffers is not available." + << std::endl; + return; + } + glBindTexture(GL_TEXTURE_2D, mTextures[0]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, mTextures[0], 0); @@ -255,6 +263,13 @@ TEST_P(DrawBuffersTest, Gaps) TEST_P(DrawBuffersTest, FirstAndLast) { + if (getClientVersion() < 3 && !extensionEnabled("GL_EXT_draw_buffers")) + { + std::cout << "Test skipped because ES3 or GL_EXT_draw_buffers is not available." + << std::endl; + return; + } + glBindTexture(GL_TEXTURE_2D, mTextures[0]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); @@ -288,6 +303,13 @@ TEST_P(DrawBuffersTest, FirstAndLast) TEST_P(DrawBuffersTest, FirstHalfNULL) { + if (getClientVersion() < 3 && !extensionEnabled("GL_EXT_draw_buffers")) + { + std::cout << "Test skipped because ES3 or GL_EXT_draw_buffers is not available." + << std::endl; + return; + } + bool flags[8] = { false }; GLenum bufs[8] = { GL_NONE }; @@ -320,6 +342,13 @@ TEST_P(DrawBuffersTest, FirstHalfNULL) TEST_P(DrawBuffersTest, UnwrittenOutputVariablesShouldNotCrash) { + if (getClientVersion() < 3 && !extensionEnabled("GL_EXT_draw_buffers")) + { + std::cout << "Test skipped because ES3 or GL_EXT_draw_buffers is not available." + << std::endl; + return; + } + // Bind two render targets but use a shader which writes only to the first one. glBindTexture(GL_TEXTURE_2D, mTextures[0]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); @@ -354,4 +383,11 @@ TEST_P(DrawBuffersTest, UnwrittenOutputVariablesShouldNotCrash) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(DrawBuffersTest, ES2_D3D11(), ES3_D3D11(), ES2_D3D11_FL9_3()); +ANGLE_INSTANTIATE_TEST(DrawBuffersTest, + ES2_D3D11(), + ES3_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/DrawElementsTest.cpp b/gfx/angle/src/tests/gl_tests/DrawElementsTest.cpp index a0e3c145ce..91f3c1e9a2 100644 --- a/gfx/angle/src/tests/gl_tests/DrawElementsTest.cpp +++ b/gfx/angle/src/tests/gl_tests/DrawElementsTest.cpp @@ -222,5 +222,5 @@ TEST_P(DrawElementsTest, DeletingAfterStreamingIndexes) ASSERT_GL_NO_ERROR(); } -ANGLE_INSTANTIATE_TEST(DrawElementsTest, ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(DrawElementsTest, ES3_OPENGL(), ES3_OPENGLES()); } diff --git a/gfx/angle/src/tests/gl_tests/ETCTextureTest.cpp b/gfx/angle/src/tests/gl_tests/ETCTextureTest.cpp new file mode 100644 index 0000000000..f34ac24b32 --- /dev/null +++ b/gfx/angle/src/tests/gl_tests/ETCTextureTest.cpp @@ -0,0 +1,79 @@ +// +// Copyright 2015 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. +// +// ETCTextureTest: +// Tests for ETC lossy decode formats. +// + +#include "test_utils/ANGLETest.h" + +using namespace angle; + +namespace +{ + +class ETCTextureTest : public ANGLETest +{ + protected: + ETCTextureTest() : mTexture(0u) + { + setWindowWidth(128); + setWindowHeight(128); + setConfigRedBits(8); + setConfigGreenBits(8); + setConfigBlueBits(8); + setConfigAlphaBits(8); + } + + void SetUp() override + { + ANGLETest::SetUp(); + + glGenTextures(1, &mTexture); + ASSERT_GL_NO_ERROR(); + } + + void TearDown() override + { + glDeleteTextures(1, &mTexture); + + ANGLETest::TearDown(); + } + + GLuint mTexture; +}; + +// Tests a texture with ETC1 lossy decode format +TEST_P(ETCTextureTest, ETC1Validation) +{ + bool supported = extensionEnabled("GL_ANGLE_lossy_etc_decode"); + + glBindTexture(GL_TEXTURE_2D, mTexture); + + GLubyte pixel[8] = {0}; + glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_ETC1_RGB8_LOSSY_DECODE_ANGLE, 4, 4, 0, + sizeof(pixel), pixel); + if (supported) + { + EXPECT_GL_NO_ERROR(); + + glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_ETC1_RGB8_LOSSY_DECODE_ANGLE, + sizeof(pixel), pixel); + EXPECT_GL_NO_ERROR(); + } + else + { + EXPECT_GL_ERROR(GL_INVALID_ENUM); + } +} + +ANGLE_INSTANTIATE_TEST(ETCTextureTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES3_D3D11(), + ES2_OPENGL(), + ES3_OPENGL()); +} // anonymous namespace diff --git a/gfx/angle/src/tests/gl_tests/FenceSyncTests.cpp b/gfx/angle/src/tests/gl_tests/FenceSyncTests.cpp index 196a3c1699..78d5b6443b 100644 --- a/gfx/angle/src/tests/gl_tests/FenceSyncTests.cpp +++ b/gfx/angle/src/tests/gl_tests/FenceSyncTests.cpp @@ -271,5 +271,12 @@ TEST_P(FenceSyncTest, BasicOperations) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(FenceNVTest, ES2_D3D9(), ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), ES3_OPENGL()); -ANGLE_INSTANTIATE_TEST(FenceSyncTest, ES3_D3D11(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(FenceNVTest, + ES2_D3D9(), + ES2_D3D11(), + ES3_D3D11(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); +ANGLE_INSTANTIATE_TEST(FenceSyncTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/FramebufferFormatsTest.cpp b/gfx/angle/src/tests/gl_tests/FramebufferFormatsTest.cpp index 05ea2136f1..5910502d29 100644 --- a/gfx/angle/src/tests/gl_tests/FramebufferFormatsTest.cpp +++ b/gfx/angle/src/tests/gl_tests/FramebufferFormatsTest.cpp @@ -11,7 +11,7 @@ using namespace angle; class FramebufferFormatsTest : public ANGLETest { protected: - FramebufferFormatsTest() + FramebufferFormatsTest() : mFramebuffer(0), mTexture(0), mRenderbuffer(0), mProgram(0) { setWindowWidth(128); setWindowHeight(128); @@ -52,20 +52,13 @@ class FramebufferFormatsTest : public ANGLETest void testTextureFormat(GLenum internalFormat, GLint minRedBits, GLint minGreenBits, GLint minBlueBits, GLint minAlphaBits) { - GLuint tex = 0; - glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); + glGenTextures(1, &mTexture); + glBindTexture(GL_TEXTURE_2D, mTexture); glTexStorage2DEXT(GL_TEXTURE_2D, 1, internalFormat, 1, 1); - GLuint fbo = 0; - glGenFramebuffers(1, &fbo); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0); - testBitCounts(fbo, minRedBits, minGreenBits, minBlueBits, minAlphaBits, 0, 0); - - glDeleteTextures(1, &tex); - glDeleteFramebuffers(1, &fbo); + testBitCounts(mFramebuffer, minRedBits, minGreenBits, minBlueBits, minAlphaBits, 0, 0); } void testRenderbufferMultisampleFormat(int minESVersion, GLenum attachmentType, GLenum internalFormat) @@ -108,33 +101,57 @@ class FramebufferFormatsTest : public ANGLETest return; } - GLuint framebufferID; - glGenFramebuffers(1, &framebufferID); - glBindFramebuffer(GL_FRAMEBUFFER, framebufferID); - - GLuint renderbufferID; - glGenRenderbuffers(1, &renderbufferID); - glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID); + glGenRenderbuffers(1, &mRenderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer); EXPECT_GL_NO_ERROR(); glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, 2, internalFormat, 128, 128); EXPECT_GL_NO_ERROR(); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachmentType, GL_RENDERBUFFER, renderbufferID); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachmentType, GL_RENDERBUFFER, mRenderbuffer); EXPECT_GL_NO_ERROR(); - - glDeleteRenderbuffers(1, &renderbufferID); - glDeleteFramebuffers(1, &framebufferID); } - virtual void SetUp() + void SetUp() override { ANGLETest::SetUp(); + + glGenFramebuffers(1, &mFramebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); } - virtual void TearDown() + void TearDown() override { ANGLETest::TearDown(); + + if (mTexture != 0) + { + glDeleteTextures(1, &mTexture); + mTexture = 0; + } + + if (mRenderbuffer != 0) + { + glDeleteRenderbuffers(1, &mRenderbuffer); + mRenderbuffer = 0; + } + + if (mFramebuffer != 0) + { + glDeleteFramebuffers(1, &mFramebuffer); + mFramebuffer = 0; + } + + if (mProgram != 0) + { + glDeleteProgram(mProgram); + mProgram = 0; + } } + + GLuint mFramebuffer; + GLuint mTexture; + GLuint mRenderbuffer; + GLuint mProgram; }; TEST_P(FramebufferFormatsTest, RGBA4) @@ -229,5 +246,56 @@ TEST_P(FramebufferFormatsTest, RenderbufferMultisample_STENCIL_INDEX8) testRenderbufferMultisampleFormat(2, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX8); } +// Test that binding an incomplete cube map is rejected by ANGLE. +TEST_P(FramebufferFormatsTest, IncompleteCubeMap) +{ + // First make a complete CubeMap. + glGenTextures(1, &mTexture); + glBindTexture(GL_TEXTURE_CUBE_MAP, mTexture); + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, + nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, + nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, + nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, + nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, + nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, + nullptr); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X, + mTexture, 0); + + // Verify the framebuffer is complete. + ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); + + // Make the CubeMap cube-incomplete. + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, + nullptr); + + // Verify the framebuffer is incomplete. + ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, + glCheckFramebufferStatus(GL_FRAMEBUFFER)); + + // Verify drawing with the incomplete framebuffer produces a GL error + const std::string &vs = "attribute vec4 position; void main() { gl_Position = position; }"; + const std::string &ps = "void main() { gl_FragColor = vec4(1, 0, 0, 1); }"; + mProgram = CompileProgram(vs, ps); + ASSERT_NE(0u, mProgram); + drawQuad(mProgram, "position", 0.5f); + ASSERT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION); +} + // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(FramebufferFormatsTest, ES2_D3D9(), ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(FramebufferFormatsTest, + ES2_D3D9(), + ES2_D3D11(), + ES3_D3D11(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp b/gfx/angle/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp index b58c48dda7..3aebde9488 100644 --- a/gfx/angle/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp +++ b/gfx/angle/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp @@ -197,4 +197,11 @@ TEST_P(FramebufferRenderMipmapTest, RenderToMipmap) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(FramebufferRenderMipmapTest, ES2_D3D9(), ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(FramebufferRenderMipmapTest, + ES2_D3D9(), + ES2_D3D11(), + ES3_D3D11(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/GLSLTest.cpp b/gfx/angle/src/tests/gl_tests/GLSLTest.cpp index 0be58e950d..9c00cf52f4 100644 --- a/gfx/angle/src/tests/gl_tests/GLSLTest.cpp +++ b/gfx/angle/src/tests/gl_tests/GLSLTest.cpp @@ -462,6 +462,24 @@ TEST_P(GLSLTest, NamelessScopedStructs) TEST_P(GLSLTest, ScopedStructsOrderBug) { +#if defined(__APPLE__) + // TODO(geofflang): Find out why this doesn't compile on Apple OpenGL drivers + // (http://anglebug.com/1292) + if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test disabled on Apple OpenGL." << std::endl; + return; + } +#endif + + // TODO(geofflang): Find out why this doesn't compile on AMD OpenGL drivers + // (http://anglebug.com/1291) + if (isAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test disabled on AMD OpenGL." << std::endl; + return; + } + const std::string fragmentShaderSource = SHADER_SOURCE ( precision mediump float; @@ -616,6 +634,14 @@ TEST_P(GLSLTest, TwoElseIfRewriting) TEST_P(GLSLTest, InvariantVaryingOut) { + // TODO(geofflang): Some OpenGL drivers have compile errors when varyings do not have matching + // invariant attributes (http://anglebug.com/1293) + if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test disabled on OpenGL." << std::endl; + return; + } + const std::string fragmentShaderSource = SHADER_SOURCE ( precision mediump float; @@ -687,6 +713,14 @@ TEST_P(GLSLTest, FrontFacingAndVarying) TEST_P(GLSLTest, InvariantVaryingIn) { + // TODO(geofflang): Some OpenGL drivers have compile errors when varyings do not have matching + // invariant attributes (http://anglebug.com/1293) + if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test disabled on OpenGL." << std::endl; + return; + } + const std::string fragmentShaderSource = SHADER_SOURCE ( precision mediump float; @@ -748,6 +782,14 @@ TEST_P(GLSLTest, InvariantGLPosition) TEST_P(GLSLTest, InvariantAll) { + // TODO(geofflang): Some OpenGL drivers have compile errors when varyings do not have matching + // invariant attributes (http://anglebug.com/1293) + if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test disabled on OpenGL." << std::endl; + return; + } + const std::string fragmentShaderSource = SHADER_SOURCE ( precision mediump float; @@ -767,6 +809,16 @@ TEST_P(GLSLTest, InvariantAll) TEST_P(GLSLTest, MaxVaryingVec4) { +#if defined(__APPLE__) + // TODO(geofflang): Find out why this doesn't compile on Apple AND OpenGL drivers + // (http://anglebug.com/1291) + if (isAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test disabled on Apple AMD OpenGL." << std::endl; + return; + } +#endif + GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); @@ -784,6 +836,13 @@ TEST_P(GLSLTest, MaxMinusTwoVaryingVec4PlusTwoSpecialVariables) TEST_P(GLSLTest, MaxMinusTwoVaryingVec4PlusThreeSpecialVariables) { + // TODO(geofflang): Figure out why this fails on OpenGL AMD (http://anglebug.com/1291) + if (isAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test disabled on OpenGL." << std::endl; + return; + } + GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); @@ -791,7 +850,9 @@ TEST_P(GLSLTest, MaxMinusTwoVaryingVec4PlusThreeSpecialVariables) VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings - 2, 0, true, true, true, true); } -TEST_P(GLSLTest, MaxVaryingVec4PlusFragCoord) +// Disabled because drivers are allowed to successfully compile shaders that have more than the +// maximum number of varyings. (http://anglebug.com/1296) +TEST_P(GLSLTest, DISABLED_MaxVaryingVec4PlusFragCoord) { GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); @@ -801,7 +862,9 @@ TEST_P(GLSLTest, MaxVaryingVec4PlusFragCoord) VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings, 0, true, false, false, false); } -TEST_P(GLSLTest, MaxVaryingVec4PlusPointCoord) +// Disabled because drivers are allowed to successfully compile shaders that have more than the +// maximum number of varyings. (http://anglebug.com/1296) +TEST_P(GLSLTest, DISABLED_MaxVaryingVec4PlusPointCoord) { GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); @@ -866,6 +929,23 @@ TEST_P(GLSLTest, TwiceMaxVaryingVec2) return; } + if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE) + { + // TODO(geofflang): Figure out why this fails on NVIDIA's GLES driver + std::cout << "Test disabled on OpenGL ES." << std::endl; + return; + } + +#if defined(__APPLE__) + // TODO(geofflang): Find out why this doesn't compile on Apple AND OpenGL drivers + // (http://anglebug.com/1291) + if (isAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test disabled on Apple AMD OpenGL." << std::endl; + return; + } +#endif + GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); @@ -881,13 +961,32 @@ TEST_P(GLSLTest, MaxVaryingVec2Arrays) return; } + if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE) + { + // TODO(geofflang): Figure out why this fails on NVIDIA's GLES driver + std::cout << "Test disabled on OpenGL ES." << std::endl; + return; + } + +#if defined(__APPLE__) + // TODO(geofflang): Find out why this doesn't compile on Apple AND OpenGL drivers + // (http://anglebug.com/1291) + if (isAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test disabled on Apple AMD OpenGL." << std::endl; + return; + } +#endif + GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); VaryingTestBase(0, 0, 0, maxVaryings, 0, 0, 0, 0, false, false, false, true); } -TEST_P(GLSLTest, MaxPlusOneVaryingVec3) +// Disabled because drivers are allowed to successfully compile shaders that have more than the +// maximum number of varyings. (http://anglebug.com/1296) +TEST_P(GLSLTest, DISABLED_MaxPlusOneVaryingVec3) { GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); @@ -895,7 +994,9 @@ TEST_P(GLSLTest, MaxPlusOneVaryingVec3) VaryingTestBase(0, 0, 0, 0, maxVaryings + 1, 0, 0, 0, false, false, false, false); } -TEST_P(GLSLTest, MaxPlusOneVaryingVec3Array) +// Disabled because drivers are allowed to successfully compile shaders that have more than the +// maximum number of varyings. (http://anglebug.com/1296) +TEST_P(GLSLTest, DISABLED_MaxPlusOneVaryingVec3Array) { GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); @@ -903,7 +1004,9 @@ TEST_P(GLSLTest, MaxPlusOneVaryingVec3Array) VaryingTestBase(0, 0, 0, 0, 0, maxVaryings / 2 + 1, 0, 0, false, false, false, false); } -TEST_P(GLSLTest, MaxVaryingVec3AndOneVec2) +// Disabled because drivers are allowed to successfully compile shaders that have more than the +// maximum number of varyings. (http://anglebug.com/1296) +TEST_P(GLSLTest, DISABLED_MaxVaryingVec3AndOneVec2) { GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); @@ -911,7 +1014,9 @@ TEST_P(GLSLTest, MaxVaryingVec3AndOneVec2) VaryingTestBase(0, 0, 1, 0, maxVaryings, 0, 0, 0, false, false, false, false); } -TEST_P(GLSLTest, MaxPlusOneVaryingVec2) +// Disabled because drivers are allowed to successfully compile shaders that have more than the +// maximum number of varyings. (http://anglebug.com/1296) +TEST_P(GLSLTest, DISABLED_MaxPlusOneVaryingVec2) { GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); @@ -919,7 +1024,9 @@ TEST_P(GLSLTest, MaxPlusOneVaryingVec2) VaryingTestBase(0, 0, 2 * maxVaryings + 1, 0, 0, 0, 0, 0, false, false, false, false); } -TEST_P(GLSLTest, MaxVaryingVec3ArrayAndMaxPlusOneFloatArray) +// Disabled because drivers are allowed to successfully compile shaders that have more than the +// maximum number of varyings. (http://anglebug.com/1296) +TEST_P(GLSLTest, DISABLED_MaxVaryingVec3ArrayAndMaxPlusOneFloatArray) { GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); @@ -1253,6 +1360,13 @@ TEST_P(GLSLTest, VerifyMaxVertexUniformVectors) // can actually be used along with the maximum number of texture samplers. TEST_P(GLSLTest, VerifyMaxVertexUniformVectorsWithSamplers) { + if (GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE || + GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE) + { + std::cout << "Test disabled on OpenGL." << std::endl; + return; + } + int maxUniforms = 10000; glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxUniforms); EXPECT_GL_NO_ERROR(); @@ -1292,6 +1406,13 @@ TEST_P(GLSLTest, VerifyMaxFragmentUniformVectors) // can actually be used along with the maximum number of texture samplers. TEST_P(GLSLTest, VerifyMaxFragmentUniformVectorsWithSamplers) { + if (GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE || + GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE) + { + std::cout << "Test disabled on OpenGL." << std::endl; + return; + } + int maxUniforms = 10000; glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxUniforms); EXPECT_GL_NO_ERROR(); @@ -1316,7 +1437,14 @@ TEST_P(GLSLTest, VerifyMaxFragmentUniformVectorsExceeded) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(GLSLTest, ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3()); +ANGLE_INSTANTIATE_TEST(GLSLTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(GLSLTest_ES3, ES3_D3D11()); +ANGLE_INSTANTIATE_TEST(GLSLTest_ES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/ImageTest.cpp b/gfx/angle/src/tests/gl_tests/ImageTest.cpp index 4459a6f65f..19b2219ca5 100644 --- a/gfx/angle/src/tests/gl_tests/ImageTest.cpp +++ b/gfx/angle/src/tests/gl_tests/ImageTest.cpp @@ -1372,5 +1372,12 @@ TEST_P(ImageTest, UpdatedData) // Use this to select which configurations (e.g. which renderer, which GLES major version) these // tests should be run against. -ANGLE_INSTANTIATE_TEST(ImageTest, ES2_D3D9(), ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(ImageTest, + ES2_D3D9(), + ES2_D3D11(), + ES3_D3D11(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); } diff --git a/gfx/angle/src/tests/gl_tests/IncompleteTextureTest.cpp b/gfx/angle/src/tests/gl_tests/IncompleteTextureTest.cpp index 7ed6bce387..d7a113915c 100644 --- a/gfx/angle/src/tests/gl_tests/IncompleteTextureTest.cpp +++ b/gfx/angle/src/tests/gl_tests/IncompleteTextureTest.cpp @@ -163,4 +163,8 @@ TEST_P(IncompleteTextureTest, UpdateTexture) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(IncompleteTextureTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL()); +ANGLE_INSTANTIATE_TEST(IncompleteTextureTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_OPENGL(), + ES2_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/IndexBufferOffsetTest.cpp b/gfx/angle/src/tests/gl_tests/IndexBufferOffsetTest.cpp index cc3cb00600..5d6e606b03 100644 --- a/gfx/angle/src/tests/gl_tests/IndexBufferOffsetTest.cpp +++ b/gfx/angle/src/tests/gl_tests/IndexBufferOffsetTest.cpp @@ -151,5 +151,6 @@ ANGLE_INSTANTIATE_TEST(IndexBufferOffsetTest, ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), - ES3_OPENGL()); - + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/IndexedPointsTest.cpp b/gfx/angle/src/tests/gl_tests/IndexedPointsTest.cpp index 3813cb5cd2..5b6be1afa6 100644 --- a/gfx/angle/src/tests/gl_tests/IndexedPointsTest.cpp +++ b/gfx/angle/src/tests/gl_tests/IndexedPointsTest.cpp @@ -386,6 +386,18 @@ TEST_P(IndexedPointsTestUInt, VertexWithColorUnsignedIntOffset3) } // TODO(geofflang): Figure out why this test fails on Intel OpenGL -ANGLE_INSTANTIATE_TEST(IndexedPointsTestUByte, ES2_D3D11(), ES2_D3D11_FL9_3()); -ANGLE_INSTANTIATE_TEST(IndexedPointsTestUShort, ES2_D3D11(), ES2_D3D11_FL9_3()); -ANGLE_INSTANTIATE_TEST(IndexedPointsTestUInt, ES2_D3D11(), ES2_D3D11_FL9_3()); +ANGLE_INSTANTIATE_TEST(IndexedPointsTestUByte, + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES2_OPENGLES()); +ANGLE_INSTANTIATE_TEST(IndexedPointsTestUShort, + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES2_OPENGLES()); +ANGLE_INSTANTIATE_TEST(IndexedPointsTestUInt, + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES2_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/InstancingTest.cpp b/gfx/angle/src/tests/gl_tests/InstancingTest.cpp index 29f123d527..633f968920 100644 --- a/gfx/angle/src/tests/gl_tests/InstancingTest.cpp +++ b/gfx/angle/src/tests/gl_tests/InstancingTest.cpp @@ -11,7 +11,7 @@ using namespace angle; class InstancingTest : public ANGLETest { protected: - InstancingTest() + InstancingTest() : mProgram(0), mVertexBuffer(0) { setWindowWidth(256); setWindowHeight(256); @@ -21,7 +21,13 @@ class InstancingTest : public ANGLETest setConfigAlphaBits(8); } - virtual void SetUp() + ~InstancingTest() override + { + glDeleteBuffers(1, &mVertexBuffer); + glDeleteProgram(mProgram); + } + + void SetUp() override { ANGLETest::SetUp(); @@ -93,10 +99,12 @@ class InstancingTest : public ANGLETest glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glGenBuffers(1, &mVertexBuffer); + ASSERT_GL_NO_ERROR(); } - GLuint setupDrawArraysTest(const std::string &vs) + void setupDrawArraysTest(const std::string &vs) { const std::string fs = SHADER_SOURCE ( @@ -107,11 +115,8 @@ class InstancingTest : public ANGLETest } ); - GLuint program = CompileProgram(vs, fs); - if (program == 0) - { - return 0; - } + mProgram = CompileProgram(vs, fs); + ASSERT_NE(0u, mProgram); // Set the viewport glViewport(0, 0, getWindowWidth(), getWindowHeight()); @@ -120,30 +125,69 @@ class InstancingTest : public ANGLETest glClear(GL_COLOR_BUFFER_BIT); // Use the program object - glUseProgram(program); - - return program; + glUseProgram(mProgram); } - void runDrawArraysTest(GLuint program, GLint first, GLsizei count, GLsizei instanceCount, float *offset) + void setupInstancedPointsTest() { - GLuint vertexBuffer; - glGenBuffers(1, &vertexBuffer); + mIndices.clear(); + mIndices.push_back(0); + mIndices.push_back(1); + mIndices.push_back(2); + mIndices.push_back(3); - glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); + // clang-format off + const std::string vs = SHADER_SOURCE + ( + attribute vec3 a_position; + attribute vec3 a_instancePos; + void main() + { + gl_Position = vec4(a_position.xyz, 1.0); + gl_Position = vec4(a_instancePos.xyz, 1.0); + gl_PointSize = 6.0; + } + ); + + const std::string fs = SHADER_SOURCE + ( + precision mediump float; + void main() + { + gl_FragColor = vec4(1.0, 0, 0, 1.0); + } + ); + // clang-format on + + mProgram = CompileProgram(vs, fs); + ASSERT_NE(0u, mProgram); + + // Set the viewport + glViewport(0, 0, getWindowWidth(), getWindowHeight()); + + // Clear the color buffer + glClear(GL_COLOR_BUFFER_BIT); + + // Use the program object + glUseProgram(mProgram); + } + + void runDrawArraysTest(GLint first, GLsizei count, GLsizei instanceCount, float *offset) + { + glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); glBufferData(GL_ARRAY_BUFFER, mInstances.size() * sizeof(mInstances[0]), &mInstances[0], GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); // Get the attribute locations - GLint positionLoc = glGetAttribLocation(program, "a_position"); - GLint instancePosLoc = glGetAttribLocation(program, "a_instancePos"); + GLint positionLoc = glGetAttribLocation(mProgram, "a_position"); + GLint instancePosLoc = glGetAttribLocation(mProgram, "a_instancePos"); // Load the vertex position glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, mNonIndexedVertices.data()); glEnableVertexAttribArray(positionLoc); // Load the instance position - glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); glVertexAttribPointer(instancePosLoc, 3, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); glEnableVertexAttribArray(instancePosLoc); @@ -152,7 +196,7 @@ class InstancingTest : public ANGLETest mVertexAttribDivisorANGLE(instancePosLoc, 1); // Offset - GLint uniformLoc = glGetUniformLocation(program, "u_offset"); + GLint uniformLoc = glGetUniformLocation(mProgram, "u_offset"); ASSERT_NE(uniformLoc, -1); glUniform3fv(uniformLoc, 1, offset); @@ -240,6 +284,9 @@ class InstancingTest : public ANGLETest std::vector mIndices; const GLfloat quadRadius = 0.30f; + + GLuint mProgram; + GLuint mVertexBuffer; }; class InstancingTestAllConfigs : public InstancingTest @@ -254,6 +301,12 @@ class InstancingTestNo9_3 : public InstancingTest InstancingTestNo9_3() {} }; +class InstancingTestPoints : public InstancingTest +{ + protected: + InstancingTestPoints() {} +}; + // This test uses a vertex shader with the first attribute (attribute zero) instanced. // On D3D9 and D3D11 FL9_3, this triggers a special codepath that rearranges the input layout sent to D3D, // to ensure that slot/stream zero of the input layout doesn't contain per-instance data. @@ -304,23 +357,112 @@ TEST_P(InstancingTestNo9_3, DrawArraysWithOffset) } ); - GLuint program = setupDrawArraysTest(vs); - ASSERT_NE(program, 0u); + setupDrawArraysTest(vs); float offset1[3] = { 0, 0, 0 }; - runDrawArraysTest(program, 0, 6, 2, offset1); + runDrawArraysTest(0, 6, 2, offset1); float offset2[3] = { 0.0f, 1.0f, 0 }; - runDrawArraysTest(program, 6, 6, 2, offset2); + runDrawArraysTest(6, 6, 2, offset2); checkQuads(); +} - glDeleteProgram(program); +// This test verifies instancing with GL_POINTS with glDrawArraysInstanced works. +// On D3D11 FL9_3, this triggers a special codepath that emulates instanced points rendering. +TEST_P(InstancingTestPoints, DrawArrays) +{ + // Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details + // On Win7, the D3D SDK Layers emits a false warning for these tests. + // This doesn't occur on Windows 10 (Version 1511) though. + ignoreD3D11SDKLayersWarnings(); + + setupInstancedPointsTest(); + + glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); + glBufferData(GL_ARRAY_BUFFER, mInstances.size() * sizeof(mInstances[0]), &mInstances[0], + GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + // Get the attribute locations + GLint positionLoc = glGetAttribLocation(mProgram, "a_position"); + GLint instancePosLoc = glGetAttribLocation(mProgram, "a_instancePos"); + + // Load the vertex position + GLfloat pos[3] = {0, 0, 0}; + glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, pos); + glEnableVertexAttribArray(positionLoc); + + // Load the instance position + glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); + glVertexAttribPointer(instancePosLoc, 3, GL_FLOAT, GL_FALSE, 0, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glEnableVertexAttribArray(instancePosLoc); + + // Enable instancing + mVertexAttribDivisorANGLE(instancePosLoc, 1); + + // Do the instanced draw + mDrawArraysInstancedANGLE(GL_POINTS, 0, 1, static_cast(mInstances.size()) / 3); + + ASSERT_GL_NO_ERROR(); + + checkQuads(); +} + +// This test verifies instancing with GL_POINTS with glDrawElementsInstanced works. +// On D3D11 FL9_3, this triggers a special codepath that emulates instanced points rendering. +TEST_P(InstancingTestPoints, DrawElements) +{ + // Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details + // On Win7, the D3D SDK Layers emits a false warning for these tests. + // This doesn't occur on Windows 10 (Version 1511) though. + ignoreD3D11SDKLayersWarnings(); + + setupInstancedPointsTest(); + + glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); + glBufferData(GL_ARRAY_BUFFER, mInstances.size() * sizeof(mInstances[0]), &mInstances[0], + GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + // Get the attribute locations + GLint positionLoc = glGetAttribLocation(mProgram, "a_position"); + GLint instancePosLoc = glGetAttribLocation(mProgram, "a_instancePos"); + + // Load the vertex position + GLfloat pos[3] = {0, 0, 0}; + glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, pos); + glEnableVertexAttribArray(positionLoc); + + // Load the instance position + glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); + glVertexAttribPointer(instancePosLoc, 3, GL_FLOAT, GL_FALSE, 0, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glEnableVertexAttribArray(instancePosLoc); + + // Enable instancing + mVertexAttribDivisorANGLE(instancePosLoc, 1); + + // Do the instanced draw + mDrawElementsInstancedANGLE(GL_POINTS, static_cast(mIndices.size()), GL_UNSIGNED_SHORT, + mIndices.data(), static_cast(mInstances.size()) / 3); + + ASSERT_GL_NO_ERROR(); + + checkQuads(); } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. // We test on D3D9 and D3D11 9_3 because they use special codepaths when attribute zero is instanced, unlike D3D11. -ANGLE_INSTANTIATE_TEST(InstancingTestAllConfigs, ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3()); +ANGLE_INSTANTIATE_TEST(InstancingTestAllConfigs, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES2_OPENGLES()); // TODO(jmadill): Figure out the situation with DrawInstanced on FL 9_3 ANGLE_INSTANTIATE_TEST(InstancingTestNo9_3, ES2_D3D9(), ES2_D3D11()); + +ANGLE_INSTANTIATE_TEST(InstancingTestPoints, ES2_D3D11(), ES2_D3D11_FL9_3()); diff --git a/gfx/angle/src/tests/gl_tests/LineLoopTest.cpp b/gfx/angle/src/tests/gl_tests/LineLoopTest.cpp index 383ec51c5d..778fee9fe2 100644 --- a/gfx/angle/src/tests/gl_tests/LineLoopTest.cpp +++ b/gfx/angle/src/tests/gl_tests/LineLoopTest.cpp @@ -134,12 +134,20 @@ class LineLoopTest : public ANGLETest TEST_P(LineLoopTest, LineLoopUByteIndices) { + // Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details + // On Win7, the D3D SDK Layers emits a false warning for these tests. + // This doesn't occur on Windows 10 (Version 1511) though. + ignoreD3D11SDKLayersWarnings(); + static const GLubyte indices[] = { 0, 7, 6, 9, 8, 0 }; runTest(GL_UNSIGNED_BYTE, 0, indices + 1); } TEST_P(LineLoopTest, LineLoopUShortIndices) { + // Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details + ignoreD3D11SDKLayersWarnings(); + static const GLushort indices[] = { 0, 7, 6, 9, 8, 0 }; runTest(GL_UNSIGNED_SHORT, 0, indices + 1); } @@ -151,12 +159,18 @@ TEST_P(LineLoopTest, LineLoopUIntIndices) return; } + // Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details + ignoreD3D11SDKLayersWarnings(); + static const GLuint indices[] = { 0, 7, 6, 9, 8, 0 }; runTest(GL_UNSIGNED_INT, 0, indices + 1); } TEST_P(LineLoopTest, LineLoopUByteIndexBuffer) { + // Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details + ignoreD3D11SDKLayersWarnings(); + static const GLubyte indices[] = { 0, 7, 6, 9, 8, 0 }; GLuint buf; @@ -171,6 +185,9 @@ TEST_P(LineLoopTest, LineLoopUByteIndexBuffer) TEST_P(LineLoopTest, LineLoopUShortIndexBuffer) { + // Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details + ignoreD3D11SDKLayersWarnings(); + static const GLushort indices[] = { 0, 7, 6, 9, 8, 0 }; GLuint buf; @@ -190,6 +207,9 @@ TEST_P(LineLoopTest, LineLoopUIntIndexBuffer) return; } + // Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details + ignoreD3D11SDKLayersWarnings(); + static const GLuint indices[] = { 0, 7, 6, 9, 8, 0 }; GLuint buf; @@ -203,4 +223,4 @@ TEST_P(LineLoopTest, LineLoopUIntIndexBuffer) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(LineLoopTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL()); +ANGLE_INSTANTIATE_TEST(LineLoopTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/MaxTextureSizeTest.cpp b/gfx/angle/src/tests/gl_tests/MaxTextureSizeTest.cpp index 16a719053a..07869f09b1 100644 --- a/gfx/angle/src/tests/gl_tests/MaxTextureSizeTest.cpp +++ b/gfx/angle/src/tests/gl_tests/MaxTextureSizeTest.cpp @@ -291,5 +291,8 @@ TEST_P(MaxTextureSizeTest, RenderToTexture) glDeleteTextures(1, &textureId); } +// TODO(geofflang): Fix the dependence on glBlitFramebufferANGLE without checks and assuming the +// default framebuffer is BGRA to enable the GL and GLES backends. (http://anglebug.com/1289) + // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. ANGLE_INSTANTIATE_TEST(MaxTextureSizeTest, ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3()); diff --git a/gfx/angle/src/tests/gl_tests/MipmapTest.cpp b/gfx/angle/src/tests/gl_tests/MipmapTest.cpp index 9f4a4963c7..fa736411dc 100644 --- a/gfx/angle/src/tests/gl_tests/MipmapTest.cpp +++ b/gfx/angle/src/tests/gl_tests/MipmapTest.cpp @@ -265,7 +265,7 @@ class MipmapTestES3 : public ANGLETest const std::string fragmentShaderSourceArray = SHADER_SOURCE ( #version 300 es\n precision highp float; - uniform sampler2DArray tex; + uniform highp sampler2DArray tex; uniform int slice; in vec2 texcoord; out vec4 out_FragColor; @@ -303,7 +303,7 @@ class MipmapTestES3 : public ANGLETest const std::string fragmentShaderSource3D = SHADER_SOURCE ( #version 300 es\n precision highp float; - uniform sampler3D tex; + uniform highp sampler3D tex; uniform float slice; uniform float lod; in vec2 texcoord; @@ -915,16 +915,24 @@ TEST_P(MipmapTestES3, MipmapsForTexture3D) glUniform1f(mTexture3DLODUniformLocation, 1.); drawQuad(m3DProgram, "position", 0.5f); EXPECT_GL_NO_ERROR(); - EXPECT_PIXEL_EQ(px, py, 127, 127, 0, 255); + EXPECT_PIXEL_NEAR(px, py, 127, 127, 0, 255, 1.0); glUseProgram(m3DProgram); glUniform1f(mTexture3DSliceUniformLocation, 0.75f); drawQuad(m3DProgram, "position", 0.5f); EXPECT_GL_NO_ERROR(); - EXPECT_PIXEL_EQ(px, py, 127, 127, 0, 255); + EXPECT_PIXEL_NEAR(px, py, 127, 127, 0, 255, 1.0); } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. // Note: we run these tests against 9_3 on WARP due to hardware driver issues on Win7 -ANGLE_INSTANTIATE_TEST(MipmapTest, ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3_WARP(), ES2_OPENGL(), ES3_OPENGL()); -ANGLE_INSTANTIATE_TEST(MipmapTestES3, ES3_D3D11()); +ANGLE_INSTANTIATE_TEST(MipmapTest, + ES2_D3D9(), + ES2_D3D11(EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE), + ES2_D3D11(EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE), + ES2_D3D11_FL9_3_WARP(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); +ANGLE_INSTANTIATE_TEST(MipmapTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/ObjectAllocationTest.cpp b/gfx/angle/src/tests/gl_tests/ObjectAllocationTest.cpp new file mode 100644 index 0000000000..b0e59f7fcf --- /dev/null +++ b/gfx/angle/src/tests/gl_tests/ObjectAllocationTest.cpp @@ -0,0 +1,54 @@ +// +// Copyright 2015 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. +// +// ObjectAllocationTest +// Tests for object allocations and lifetimes. +// + +#include "test_utils/ANGLETest.h" + +using namespace angle; + +namespace +{ + +class ObjectAllocationTest : public ANGLETest +{ + protected: + ObjectAllocationTest() {} +}; + +// Test that we don't re-allocate a bound framebuffer ID. +TEST_P(ObjectAllocationTest, BindFramebufferBeforeGen) +{ + glBindFramebuffer(GL_FRAMEBUFFER, 1); + GLuint fbo = 0; + glGenFramebuffers(1, &fbo); + EXPECT_NE(1u, fbo); + glDeleteFramebuffers(1, &fbo); + EXPECT_GL_NO_ERROR(); +} + +// Test that we don't re-allocate a bound framebuffer ID, other pattern. +TEST_P(ObjectAllocationTest, BindFramebufferAfterGen) +{ + GLuint firstFBO = 0; + glGenFramebuffers(1, &firstFBO); + glBindFramebuffer(GL_FRAMEBUFFER, 1); + glDeleteFramebuffers(1, &firstFBO); + + glBindFramebuffer(GL_FRAMEBUFFER, 2); + GLuint secondFBOs[2] = {0}; + glGenFramebuffers(2, secondFBOs); + EXPECT_NE(2u, secondFBOs[0]); + EXPECT_NE(2u, secondFBOs[1]); + glDeleteFramebuffers(2, secondFBOs); + + EXPECT_GL_NO_ERROR(); +} + +} // anonymous namespace + +ANGLE_INSTANTIATE_TEST(ObjectAllocationTest, ES3_OPENGL(), ES3_D3D11()); \ No newline at end of file diff --git a/gfx/angle/src/tests/gl_tests/OcclusionQueriesTest.cpp b/gfx/angle/src/tests/gl_tests/OcclusionQueriesTest.cpp index e56a1c35c5..874cf4cef7 100644 --- a/gfx/angle/src/tests/gl_tests/OcclusionQueriesTest.cpp +++ b/gfx/angle/src/tests/gl_tests/OcclusionQueriesTest.cpp @@ -13,7 +13,7 @@ using namespace angle; class OcclusionQueriesTest : public ANGLETest { protected: - OcclusionQueriesTest() + OcclusionQueriesTest() : mProgram(0), mRNG(1) { setWindowWidth(128); setWindowHeight(128); @@ -22,11 +22,9 @@ class OcclusionQueriesTest : public ANGLETest setConfigBlueBits(8); setConfigAlphaBits(8); setConfigDepthBits(24); - - mProgram = 0; } - virtual void SetUp() + void SetUp() override { ANGLETest::SetUp(); @@ -49,13 +47,10 @@ class OcclusionQueriesTest : public ANGLETest ); mProgram = CompileProgram(passthroughVS, passthroughPS); - if (mProgram == 0) - { - FAIL() << "shader compilation failed."; - } + ASSERT_NE(0u, mProgram); } - virtual void TearDown() + void TearDown() override { glDeleteProgram(mProgram); @@ -63,6 +58,7 @@ class OcclusionQueriesTest : public ANGLETest } GLuint mProgram; + RNG mRNG; }; TEST_P(OcclusionQueriesTest, IsOccluded) @@ -328,8 +324,8 @@ TEST_P(OcclusionQueriesTest, MultiContext) { eglMakeCurrent(display, surface, surface, context.context); - float depth = - context.visiblePasses[pass] ? RandomBetween(0.0f, 0.4f) : RandomBetween(0.6f, 1.0f); + float depth = context.visiblePasses[pass] ? mRNG.randomFloatBetween(0.0f, 0.4f) + : mRNG.randomFloatBetween(0.6f, 1.0f); drawQuad(context.program, "position", depth); EXPECT_GL_NO_ERROR(); @@ -365,4 +361,6 @@ ANGLE_INSTANTIATE_TEST(OcclusionQueriesTest, ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), - ES3_OPENGL()); + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/PBOExtensionTest.cpp b/gfx/angle/src/tests/gl_tests/PBOExtensionTest.cpp index 9f5615f5ea..f0c0bd3930 100644 --- a/gfx/angle/src/tests/gl_tests/PBOExtensionTest.cpp +++ b/gfx/angle/src/tests/gl_tests/PBOExtensionTest.cpp @@ -150,4 +150,4 @@ TEST_P(PBOExtensionTest, PBOWithExistingData) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(PBOExtensionTest, ES2_D3D11(), ES3_D3D11()); +ANGLE_INSTANTIATE_TEST(PBOExtensionTest, ES2_D3D11(), ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/PackUnpackTest.cpp b/gfx/angle/src/tests/gl_tests/PackUnpackTest.cpp index caa743d569..b83252322d 100644 --- a/gfx/angle/src/tests/gl_tests/PackUnpackTest.cpp +++ b/gfx/angle/src/tests/gl_tests/PackUnpackTest.cpp @@ -201,7 +201,13 @@ TEST_P(PackUnpackTest, PackUnpackSnormOverflow) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(PackUnpackTest, ES3_OPENGL(3, 3), ES3_OPENGL(4, 0), ES3_OPENGL(4, 1), ES3_OPENGL(4, 2), - ES3_OPENGL(4, 3), ES3_OPENGL(4, 4), ES3_OPENGL(4, 5)); - +ANGLE_INSTANTIATE_TEST(PackUnpackTest, + ES3_OPENGL(3, 3), + ES3_OPENGL(4, 0), + ES3_OPENGL(4, 1), + ES3_OPENGL(4, 2), + ES3_OPENGL(4, 3), + ES3_OPENGL(4, 4), + ES3_OPENGL(4, 5), + ES3_OPENGLES()); } diff --git a/gfx/angle/src/tests/gl_tests/PbufferTest.cpp b/gfx/angle/src/tests/gl_tests/PbufferTest.cpp index ab08078c9d..fb381e1430 100644 --- a/gfx/angle/src/tests/gl_tests/PbufferTest.cpp +++ b/gfx/angle/src/tests/gl_tests/PbufferTest.cpp @@ -334,4 +334,10 @@ TEST_P(PbufferTest, BindTexImageAndRedefineTexture) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(PbufferTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_D3D11_WARP(), ES2_D3D11_REFERENCE()); +ANGLE_INSTANTIATE_TEST(PbufferTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_OPENGL(), + ES2_D3D11_WARP(), + ES2_D3D11_REFERENCE(), + ES2_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/PointSpritesTest.cpp b/gfx/angle/src/tests/gl_tests/PointSpritesTest.cpp index 2ad2e01b36..6b7f26555b 100644 --- a/gfx/angle/src/tests/gl_tests/PointSpritesTest.cpp +++ b/gfx/angle/src/tests/gl_tests/PointSpritesTest.cpp @@ -154,6 +154,7 @@ TEST_P(PointSpritesTest, PointCoordAndPointSizeCompliance) // https://www.khronos.org/registry/webgl/sdk/tests/conformance/rendering/point-no-attributes.html TEST_P(PointSpritesTest, PointWithoutAttributesCompliance) { + // clang-format off const std::string fs = SHADER_SOURCE ( precision mediump float; @@ -167,10 +168,11 @@ TEST_P(PointSpritesTest, PointWithoutAttributesCompliance) ( void main() { - gl_PointSize = 1.0; + gl_PointSize = 2.0; gl_Position = vec4(0.0, 0.0, 0.0, 1.0); } ); + // clang-format on GLuint program = CompileProgram(vs, fs); ASSERT_NE(program, 0u); @@ -438,4 +440,9 @@ TEST_P(PointSpritesTest, PointSizeDeclaredButUnused) // We test on D3D11 9_3 because the existing D3D11 PointSprite implementation // uses Geometry Shaders which are not supported for 9_3. // D3D9 and D3D11 are also tested to ensure no regressions. -ANGLE_INSTANTIATE_TEST(PointSpritesTest, ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3()); +ANGLE_INSTANTIATE_TEST(PointSpritesTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES2_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/ProvokingVertexTest.cpp b/gfx/angle/src/tests/gl_tests/ProvokingVertexTest.cpp index ff4564be8d..1e7fe074e4 100644 --- a/gfx/angle/src/tests/gl_tests/ProvokingVertexTest.cpp +++ b/gfx/angle/src/tests/gl_tests/ProvokingVertexTest.cpp @@ -140,6 +140,15 @@ TEST_P(ProvokingVertexTest, FlatTriangle) // Ensure that any provoking vertex shenanigans still gives correct vertex streams. TEST_P(ProvokingVertexTest, FlatTriWithTransformFeedback) { + // TODO(cwallez) figure out why it is broken on AMD on Mac +#if defined(ANGLE_PLATFORM_APPLE) + if (isAMD()) + { + std::cout << "Test skipped on AMD on Mac." << std::endl; + return; + } +#endif + glGenTransformFeedbacks(1, &mTransformFeedback); glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback); @@ -300,6 +309,6 @@ TEST_P(ProvokingVertexTest, FlatTriStripPrimitiveRestart) } } -ANGLE_INSTANTIATE_TEST(ProvokingVertexTest, ES3_D3D11(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(ProvokingVertexTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); } // anonymous namespace diff --git a/gfx/angle/src/tests/gl_tests/QueryDisplayAttribTest.cpp b/gfx/angle/src/tests/gl_tests/QueryDisplayAttribTest.cpp deleted file mode 100644 index 86d67bce5e..0000000000 --- a/gfx/angle/src/tests/gl_tests/QueryDisplayAttribTest.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// -// Copyright 2015 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 "test_utils/ANGLETest.h" -#include "com_utils.h" - -using namespace angle; - -class QueryDisplayAttributeTest : public ANGLETest -{ - protected: - QueryDisplayAttributeTest() - { - mQueryDisplayAttribEXT = nullptr; - mQueryDeviceAttribEXT = nullptr; - mQueryDeviceStringEXT = nullptr; - } - - void SetUp() override - { - ANGLETest::SetUp(); - - const char *extensionString = static_cast(eglQueryString(getEGLWindow()->getDisplay(), EGL_EXTENSIONS)); - if (strstr(extensionString, "EGL_EXT_device_query")) - { - mQueryDisplayAttribEXT = (PFNEGLQUERYDISPLAYATTRIBEXTPROC)eglGetProcAddress("eglQueryDisplayAttribEXT"); - mQueryDeviceAttribEXT = (PFNEGLQUERYDEVICEATTRIBEXTPROC)eglGetProcAddress("eglQueryDeviceAttribEXT"); - mQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)eglGetProcAddress("eglQueryDeviceStringEXT"); - } - - if (!mQueryDeviceStringEXT) - { - FAIL() << "ANGLE extension EGL_EXT_device_query export eglQueryDeviceStringEXT was not found"; - } - - if (!mQueryDisplayAttribEXT) - { - FAIL() << "ANGLE extension EGL_EXT_device_query export eglQueryDisplayAttribEXT was not found"; - } - - if (!mQueryDeviceAttribEXT) - { - FAIL() << "ANGLE extension EGL_EXT_device_query export eglQueryDeviceAttribEXT was not found"; - } - - EGLAttrib angleDevice = 0; - EXPECT_EQ(EGL_TRUE, mQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); - extensionString = static_cast(mQueryDeviceStringEXT(reinterpret_cast(angleDevice), EGL_EXTENSIONS)); - if (strstr(extensionString, "EGL_ANGLE_device_d3d") == NULL) - { - FAIL() << "ANGLE extension EGL_ANGLE_device_d3d was not found"; - } - } - - void TearDown() override - { - ANGLETest::TearDown(); - } - - PFNEGLQUERYDISPLAYATTRIBEXTPROC mQueryDisplayAttribEXT; - PFNEGLQUERYDEVICEATTRIBEXTPROC mQueryDeviceAttribEXT; - PFNEGLQUERYDEVICESTRINGEXTPROC mQueryDeviceStringEXT; -}; - -// This test attempts to obtain a D3D11 device and a D3D9 device using the eglQueryDeviceAttribEXT function. -// If the test is configured to use D3D11 then it should succeed to obtain a D3D11 device. -// If the test is confitured to use D3D9, then it should succeed to obtain a D3D9 device. -TEST_P(QueryDisplayAttributeTest, QueryDevice) -{ - EGLAttrib device = 0; - EGLAttrib angleDevice = 0; - if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) - { - EXPECT_EQ(EGL_TRUE, mQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); - EXPECT_EQ(EGL_TRUE, mQueryDeviceAttribEXT(reinterpret_cast(angleDevice), EGL_D3D11_DEVICE_ANGLE, &device)); - ID3D11Device *d3d11Device = reinterpret_cast(device); - IDXGIDevice *dxgiDevice = DynamicCastComObject(d3d11Device); - EXPECT_TRUE(dxgiDevice != nullptr); - SafeRelease(dxgiDevice); - } - - if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE) - { - EXPECT_EQ(EGL_TRUE, mQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); - EXPECT_EQ(EGL_TRUE, mQueryDeviceAttribEXT(reinterpret_cast(angleDevice), EGL_D3D9_DEVICE_ANGLE, &device)); - IDirect3DDevice9 *d3d9Device = reinterpret_cast(device); - IDirect3D9 *d3d9 = nullptr; - EXPECT_EQ(S_OK, d3d9Device->GetDirect3D(&d3d9)); - EXPECT_TRUE(d3d9 != nullptr); - SafeRelease(d3d9); - } -} - -// This test attempts to obtain a D3D11 device from a D3D9 configured system and a D3D9 device from -// a D3D11 configured system using the eglQueryDeviceAttribEXT function. -// If the test is configured to use D3D11 then it should fail to obtain a D3D11 device. -// If the test is confitured to use D3D9, then it should fail to obtain a D3D9 device. -TEST_P(QueryDisplayAttributeTest, QueryDeviceBadAttrbiute) -{ - EGLAttrib device = 0; - EGLAttrib angleDevice = 0; - if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) - { - EXPECT_EQ(EGL_TRUE, mQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); - EXPECT_EQ(EGL_FALSE, mQueryDeviceAttribEXT(reinterpret_cast(angleDevice), EGL_D3D9_DEVICE_ANGLE, &device)); - } - - if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE) - { - EXPECT_EQ(EGL_TRUE, mQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); - EXPECT_EQ(EGL_FALSE, mQueryDeviceAttribEXT(reinterpret_cast(angleDevice), EGL_D3D11_DEVICE_ANGLE, &device)); - } -} - -// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(QueryDisplayAttributeTest, ES2_D3D9(), ES2_D3D11()); diff --git a/gfx/angle/src/tests/gl_tests/ReadPixelsTest.cpp b/gfx/angle/src/tests/gl_tests/ReadPixelsTest.cpp index bf48c55336..8d5c077a97 100644 --- a/gfx/angle/src/tests/gl_tests/ReadPixelsTest.cpp +++ b/gfx/angle/src/tests/gl_tests/ReadPixelsTest.cpp @@ -3,11 +3,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // +// ReadPixelsTest: +// Tests calls related to glReadPixels. +// #include "test_utils/ANGLETest.h" +#include + +#include "random_utils.h" + using namespace angle; +namespace +{ + class ReadPixelsTest : public ANGLETest { protected: @@ -20,77 +30,9 @@ class ReadPixelsTest : public ANGLETest setConfigBlueBits(8); setConfigAlphaBits(8); } - - virtual void SetUp() - { - ANGLETest::SetUp(); - - glGenBuffers(1, &mPBO); - glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); - glBufferData(GL_PIXEL_PACK_BUFFER, 4 * getWindowWidth() * getWindowHeight(), NULL, GL_STATIC_DRAW); - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - - const char *vertexShaderSrc = SHADER_SOURCE - ( - attribute vec4 aTest; - attribute vec2 aPosition; - varying vec4 vTest; - - void main() - { - vTest = aTest; - gl_Position = vec4(aPosition, 0.0, 1.0); - gl_PointSize = 1.0; - } - ); - - const char *fragmentShaderSrc = SHADER_SOURCE - ( - precision mediump float; - varying vec4 vTest; - - void main() - { - gl_FragColor = vTest; - } - ); - - mProgram = CompileProgram(vertexShaderSrc, fragmentShaderSrc); - - glGenTextures(1, &mTexture); - glBindTexture(GL_TEXTURE_2D, mTexture); - glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 1); - - glGenFramebuffers(1, &mFBO); - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - glGenBuffers(1, &mPositionVBO); - glBindBuffer(GL_ARRAY_BUFFER, mPositionVBO); - glBufferData(GL_ARRAY_BUFFER, 128, NULL, GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - ASSERT_GL_NO_ERROR(); - } - - virtual void TearDown() - { - ANGLETest::TearDown(); - - glDeleteBuffers(1, &mPBO); - glDeleteProgram(mProgram); - glDeleteTextures(1, &mTexture); - glDeleteFramebuffers(1, &mFBO); - } - - GLuint mPBO; - GLuint mProgram; - GLuint mTexture; - GLuint mFBO; - GLuint mPositionVBO; }; +// Test out of bounds reads. TEST_P(ReadPixelsTest, OutOfBounds) { glClearColor(1.0f, 0.0f, 0.0f, 1.0f); @@ -124,7 +66,49 @@ TEST_P(ReadPixelsTest, OutOfBounds) } } -TEST_P(ReadPixelsTest, PBOWithOtherTarget) +class ReadPixelsPBOTest : public ReadPixelsTest +{ + protected: + ReadPixelsPBOTest() : mPBO(0), mTexture(0), mFBO(0) {} + + void SetUp() override + { + ANGLETest::SetUp(); + + glGenBuffers(1, &mPBO); + glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); + glBufferData(GL_PIXEL_PACK_BUFFER, 4 * getWindowWidth() * getWindowHeight(), nullptr, + GL_STATIC_DRAW); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + + glGenTextures(1, &mTexture); + glBindTexture(GL_TEXTURE_2D, mTexture); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 1); + + glGenFramebuffers(1, &mFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + ASSERT_GL_NO_ERROR(); + } + + void TearDown() override + { + ANGLETest::TearDown(); + + glDeleteBuffers(1, &mPBO); + glDeleteTextures(1, &mTexture); + glDeleteFramebuffers(1, &mFBO); + } + + GLuint mPBO; + GLuint mTexture; + GLuint mFBO; +}; + +// Test that binding a PBO to ARRAY_BUFFER works as expected. +TEST_P(ReadPixelsPBOTest, ArrayBufferTarget) { glClearColor(1.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); @@ -149,8 +133,16 @@ TEST_P(ReadPixelsTest, PBOWithOtherTarget) EXPECT_GL_NO_ERROR(); } -TEST_P(ReadPixelsTest, PBOWithExistingData) +// Test that using a PBO does not overwrite existing data. +TEST_P(ReadPixelsPBOTest, ExistingDataPreserved) { + // TODO(geofflang): Figure out why this fails on AMD OpenGL (http://anglebug.com/1291) + if (isAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test disabled on AMD OpenGL." << std::endl; + return; + } + // Clear backbuffer to red glClearColor(1.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); @@ -187,7 +179,8 @@ TEST_P(ReadPixelsTest, PBOWithExistingData) EXPECT_GL_NO_ERROR(); } -TEST_P(ReadPixelsTest, PBOAndSubData) +// Test that calling SubData preserves PBO data. +TEST_P(ReadPixelsPBOTest, SubDataPreservesContents) { glClearColor(1.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); @@ -215,7 +208,8 @@ TEST_P(ReadPixelsTest, PBOAndSubData) EXPECT_GL_NO_ERROR(); } -TEST_P(ReadPixelsTest, PBOAndSubDataOffset) +// Same as the prior test, but with an offset. +TEST_P(ReadPixelsPBOTest, SubDataOffsetPreservesContents) { glClearColor(1.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); @@ -248,7 +242,53 @@ TEST_P(ReadPixelsTest, PBOAndSubDataOffset) EXPECT_GL_NO_ERROR(); } -TEST_P(ReadPixelsTest, DrawWithPBO) +class ReadPixelsPBODrawTest : public ReadPixelsPBOTest +{ + protected: + ReadPixelsPBODrawTest() : mProgram(0), mPositionVBO(0) {} + + void SetUp() override + { + ReadPixelsPBOTest::SetUp(); + + const char *vertexShaderSrc = + "attribute vec4 aTest; attribute vec2 aPosition; varying vec4 vTest;\n" + "void main()\n" + "{\n" + " vTest = aTest;\n" + " gl_Position = vec4(aPosition, 0.0, 1.0);\n" + " gl_PointSize = 1.0;\n" + "}"; + + const char *fragmentShaderSrc = + "precision mediump float; varying vec4 vTest;\n" + "void main()\n" + "{\n" + " gl_FragColor = vTest;\n" + "}"; + + mProgram = CompileProgram(vertexShaderSrc, fragmentShaderSrc); + ASSERT_NE(0u, mProgram); + + glGenBuffers(1, &mPositionVBO); + glBindBuffer(GL_ARRAY_BUFFER, mPositionVBO); + glBufferData(GL_ARRAY_BUFFER, 128, NULL, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + void TearDown() override + { + glDeleteProgram(mProgram); + glDeleteBuffers(1, &mPositionVBO); + ReadPixelsPBOTest::TearDown(); + } + + GLuint mProgram; + GLuint mPositionVBO; +}; + +// Test that we can draw with PBO data. +TEST_P(ReadPixelsPBODrawTest, DrawWithPBO) { unsigned char data[4] = { 1, 2, 3, 4 }; @@ -256,7 +296,6 @@ TEST_P(ReadPixelsTest, DrawWithPBO) glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data); EXPECT_GL_NO_ERROR(); - // glReadBuffer(GL_COLOR_ATTACHMENT0); // FIXME: currently UNIMPLEMENTED glBindFramebuffer(GL_READ_FRAMEBUFFER, mFBO); EXPECT_GL_NO_ERROR(); @@ -301,7 +340,46 @@ TEST_P(ReadPixelsTest, DrawWithPBO) EXPECT_EQ(4, data[3]); } -TEST_P(ReadPixelsTest, MultisampledPBO) +class ReadPixelsMultisampleTest : public ReadPixelsTest +{ + protected: + ReadPixelsMultisampleTest() : mFBO(0), mRBO(0), mPBO(0) {} + + void SetUp() override + { + ANGLETest::SetUp(); + + glGenFramebuffers(1, &mFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + + glGenRenderbuffers(1, &mRBO); + glBindRenderbuffer(GL_RENDERBUFFER, mRBO); + + glGenBuffers(1, &mPBO); + glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); + glBufferData(GL_PIXEL_PACK_BUFFER, 4 * getWindowWidth() * getWindowHeight(), nullptr, + GL_STATIC_DRAW); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + + ASSERT_GL_NO_ERROR(); + } + + void TearDown() override + { + ANGLETest::TearDown(); + + glDeleteFramebuffers(1, &mFBO); + glDeleteRenderbuffers(1, &mRBO); + glDeleteBuffers(1, &mPBO); + } + + GLuint mFBO; + GLuint mRBO; + GLuint mPBO; +}; + +// Test ReadPixels from a multisampled framebuffer. +TEST_P(ReadPixelsMultisampleTest, BasicClear) { if (getClientVersion() < 3 && !extensionEnabled("GL_ANGLE_framebuffer_multisample")) { @@ -309,14 +387,6 @@ TEST_P(ReadPixelsTest, MultisampledPBO) return; } - GLuint fbo; - glGenFramebuffers(1, &fbo); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - - GLuint rbo; - glGenRenderbuffers(1, &rbo); - glBindRenderbuffer(GL_RENDERBUFFER, rbo); - if (extensionEnabled("GL_ANGLE_framebuffer_multisample")) { glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, 2, GL_RGBA8, 4, 4); @@ -326,24 +396,243 @@ TEST_P(ReadPixelsTest, MultisampledPBO) glRenderbufferStorageMultisample(GL_RENDERBUFFER, 2, GL_RGBA8, 4, 4); } - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); - + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRBO); ASSERT_GL_NO_ERROR(); glClearColor(1.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); - EXPECT_GL_NO_ERROR(); glReadPixels(0, 0, 1, 1, GL_RGBA8, GL_UNSIGNED_BYTE, NULL); - EXPECT_GL_ERROR(GL_INVALID_OPERATION); - - glDeleteRenderbuffers(1, &rbo); - glDeleteFramebuffers(1, &fbo); } +class ReadPixelsTextureTest : public ANGLETest +{ + public: + ReadPixelsTextureTest() : mFBO(0), mTexture(0) + { + setWindowWidth(32); + setWindowHeight(32); + setConfigRedBits(8); + setConfigGreenBits(8); + setConfigBlueBits(8); + setConfigAlphaBits(8); + } + + void SetUp() override + { + ANGLETest::SetUp(); + + glGenTextures(1, &mTexture); + glGenFramebuffers(1, &mFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + } + + void TearDown() override + { + glDeleteFramebuffers(1, &mFBO); + glDeleteTextures(1, &mTexture); + + ANGLETest::TearDown(); + } + + void initTexture(GLenum textureTarget, + GLint levels, + GLint attachmentLevel, + GLint attachmentLayer) + { + glBindTexture(textureTarget, mTexture); + glTexStorage3D(textureTarget, levels, GL_RGBA8, 4, 4, 4); + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTexture, attachmentLevel, + attachmentLayer); + initializeTextureData(textureTarget, levels); + } + + void testRead(GLenum textureTarget, GLint levels, GLint attachmentLevel, GLint attachmentLayer) + { + initTexture(textureTarget, levels, attachmentLevel, attachmentLayer); + verifyColor(attachmentLevel, attachmentLayer); + } + + void initPBO() + { + glGenBuffers(1, &mBuffer); + glBindBuffer(GL_PIXEL_PACK_BUFFER, mBuffer); + glBufferData(GL_PIXEL_PACK_BUFFER, sizeof(angle::GLColor), nullptr, GL_STREAM_COPY); + ASSERT_GL_NO_ERROR(); + } + + void testPBORead(GLenum textureTarget, + GLint levels, + GLint attachmentLevel, + GLint attachmentLayer) + { + initPBO(); + initTexture(textureTarget, levels, attachmentLevel, attachmentLayer); + verifyPBO(attachmentLevel, attachmentLayer); + } + + // Give each {level,layer} pair a (probably) unique color via random. + GLuint getColorValue(GLint level, GLint layer) + { + mRNG.reseed(level + layer * 32); + return mRNG.randomUInt(); + } + + void verifyColor(GLint level, GLint layer) + { + angle::GLColor colorValue(getColorValue(level, layer)); + EXPECT_PIXEL_COLOR_EQ(0, 0, colorValue); + } + + void verifyPBO(GLint level, GLint layer) + { + glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + + angle::GLColor expectedColor(getColorValue(level, layer)); + void *mapPointer = + glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, sizeof(angle::GLColor), GL_MAP_READ_BIT); + ASSERT_NE(nullptr, mapPointer); + angle::GLColor actualColor; + memcpy(&actualColor, mapPointer, sizeof(angle::GLColor)); + glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(expectedColor, actualColor); + } + + void initializeTextureData(GLenum textureTarget, GLint levels) + { + for (GLint level = 0; level < levels; ++level) + { + GLint mipSize = 4 >> level; + GLint layers = (textureTarget == GL_TEXTURE_3D ? mipSize : 4); + + size_t layerSize = mipSize * mipSize; + std::vector textureData(layers * layerSize); + + for (GLint layer = 0; layer < layers; ++layer) + { + GLuint colorValue = getColorValue(level, layer); + size_t offset = (layer * layerSize); + std::fill(textureData.begin() + offset, textureData.begin() + offset + layerSize, + colorValue); + } + + glTexSubImage3D(textureTarget, level, 0, 0, 0, mipSize, mipSize, layers, GL_RGBA, + GL_UNSIGNED_BYTE, textureData.data()); + } + } + + angle::RNG mRNG; + GLuint mFBO; + GLuint mTexture; + GLuint mBuffer; +}; + +// Test 3D attachment readback. +TEST_P(ReadPixelsTextureTest, BasicAttachment3D) +{ + testRead(GL_TEXTURE_3D, 1, 0, 0); +} + +// Test 3D attachment readback, non-zero mip. +TEST_P(ReadPixelsTextureTest, MipAttachment3D) +{ + testRead(GL_TEXTURE_3D, 2, 1, 0); +} + +// Test 3D attachment readback, non-zero layer. +TEST_P(ReadPixelsTextureTest, LayerAttachment3D) +{ + testRead(GL_TEXTURE_3D, 1, 0, 1); +} + +// Test 3D attachment readback, non-zero mip and layer. +TEST_P(ReadPixelsTextureTest, MipLayerAttachment3D) +{ + testRead(GL_TEXTURE_3D, 2, 1, 1); +} + +// Test 2D array attachment readback. +TEST_P(ReadPixelsTextureTest, BasicAttachment2DArray) +{ + testRead(GL_TEXTURE_2D_ARRAY, 1, 0, 0); +} + +// Test 3D attachment readback, non-zero mip. +TEST_P(ReadPixelsTextureTest, MipAttachment2DArray) +{ + testRead(GL_TEXTURE_2D_ARRAY, 2, 1, 0); +} + +// Test 3D attachment readback, non-zero layer. +TEST_P(ReadPixelsTextureTest, LayerAttachment2DArray) +{ + testRead(GL_TEXTURE_2D_ARRAY, 1, 0, 1); +} + +// Test 3D attachment readback, non-zero mip and layer. +TEST_P(ReadPixelsTextureTest, MipLayerAttachment2DArray) +{ + testRead(GL_TEXTURE_2D_ARRAY, 2, 1, 1); +} + +// Test 3D attachment PBO readback. +TEST_P(ReadPixelsTextureTest, BasicAttachment3DPBO) +{ + testPBORead(GL_TEXTURE_3D, 1, 0, 0); +} + +// Test 3D attachment readback, non-zero mip. +TEST_P(ReadPixelsTextureTest, MipAttachment3DPBO) +{ + testPBORead(GL_TEXTURE_3D, 2, 1, 0); +} + +// Test 3D attachment readback, non-zero layer. +TEST_P(ReadPixelsTextureTest, LayerAttachment3DPBO) +{ + testPBORead(GL_TEXTURE_3D, 1, 0, 1); +} + +// Test 3D attachment readback, non-zero mip and layer. +TEST_P(ReadPixelsTextureTest, MipLayerAttachment3DPBO) +{ + testPBORead(GL_TEXTURE_3D, 2, 1, 1); +} + +// Test 2D array attachment readback. +TEST_P(ReadPixelsTextureTest, BasicAttachment2DArrayPBO) +{ + testPBORead(GL_TEXTURE_2D_ARRAY, 1, 0, 0); +} + +// Test 3D attachment readback, non-zero mip. +TEST_P(ReadPixelsTextureTest, MipAttachment2DArrayPBO) +{ + testPBORead(GL_TEXTURE_2D_ARRAY, 2, 1, 0); +} + +// Test 3D attachment readback, non-zero layer. +TEST_P(ReadPixelsTextureTest, LayerAttachment2DArrayPBO) +{ + testPBORead(GL_TEXTURE_2D_ARRAY, 1, 0, 1); +} + +// Test 3D attachment readback, non-zero mip and layer. +TEST_P(ReadPixelsTextureTest, MipLayerAttachment2DArrayPBO) +{ + testPBORead(GL_TEXTURE_2D_ARRAY, 2, 1, 1); +} + +} // anonymous namespace + // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(ReadPixelsTest, ES3_D3D11()); +ANGLE_INSTANTIATE_TEST(ReadPixelsTest, ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES()); +ANGLE_INSTANTIATE_TEST(ReadPixelsPBOTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); +ANGLE_INSTANTIATE_TEST(ReadPixelsPBODrawTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); +ANGLE_INSTANTIATE_TEST(ReadPixelsMultisampleTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); +ANGLE_INSTANTIATE_TEST(ReadPixelsTextureTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); diff --git a/gfx/angle/src/tests/gl_tests/RendererTest.cpp b/gfx/angle/src/tests/gl_tests/RendererTest.cpp index 9456d833b8..1b07acbf7d 100644 --- a/gfx/angle/src/tests/gl_tests/RendererTest.cpp +++ b/gfx/angle/src/tests/gl_tests/RendererTest.cpp @@ -127,13 +127,84 @@ TEST_P(RendererTest, SimpleOperation) // Select configurations (e.g. which renderer, which GLES major version) these tests should be run against. ANGLE_INSTANTIATE_TEST(RendererTest, - ES2_D3D9(), ES2_D3D9_REFERENCE(), - ES2_D3D11(), ES2_D3D11_FL11_0(), ES2_D3D11_FL10_1(), ES2_D3D11_FL10_0(), ES2_D3D11_FL9_3(), - ES2_D3D11_WARP(), ES2_D3D11_FL11_0_WARP(), ES2_D3D11_FL10_1_WARP(), ES2_D3D11_FL10_0_WARP(), ES2_D3D11_FL9_3_WARP(), - ES2_D3D11_REFERENCE(), ES2_D3D11_FL11_0_REFERENCE(), ES2_D3D11_FL10_1_REFERENCE(), ES2_D3D11_FL10_0_REFERENCE(), ES2_D3D11_FL9_3_REFERENCE(), - ES3_D3D11(), ES3_D3D11_FL11_0(), ES3_D3D11_FL10_1(), ES3_D3D11_FL10_0(), - ES3_D3D11_WARP(), ES3_D3D11_FL11_0_WARP(), ES3_D3D11_FL10_1_WARP(), ES3_D3D11_FL10_0_WARP(), - ES3_D3D11_REFERENCE(), ES3_D3D11_FL11_0_REFERENCE(), ES3_D3D11_FL10_1_REFERENCE(), ES3_D3D11_FL10_0_REFERENCE(), - ES2_OPENGL(), ES3_OPENGL()); + // ES2 on top of D3D9 + ES2_D3D9(), + ES2_D3D9_REFERENCE(), + // ES2 on top of D3D11 feature level 9.3 to 11.0 + ES2_D3D11(), + ES2_D3D11_FL11_0(), + ES2_D3D11_FL10_1(), + ES2_D3D11_FL10_0(), + ES2_D3D11_FL9_3(), + + // ES2 on top of D3D11 WARP feature level 9.3 to 11.0 + ES2_D3D11_WARP(), + ES2_D3D11_FL11_0_WARP(), + ES2_D3D11_FL10_1_WARP(), + ES2_D3D11_FL10_0_WARP(), + ES2_D3D11_FL9_3_WARP(), + + // ES2 on top of D3D11 reference feature level 9.3 to 11.0 + ES2_D3D11_REFERENCE(), + ES2_D3D11_FL11_0_REFERENCE(), + ES2_D3D11_FL10_1_REFERENCE(), + ES2_D3D11_FL10_0_REFERENCE(), + ES2_D3D11_FL9_3_REFERENCE(), + + // ES3 on top of D3D11 feature level 9.3 to 11.0 + ES3_D3D11(), + ES3_D3D11_FL11_0(), + ES3_D3D11_FL10_1(), + ES3_D3D11_FL10_0(), + + // ES3 on top of D3D11 WARP feature level 9.3 to 11.0 + ES3_D3D11_WARP(), + ES3_D3D11_FL11_0_WARP(), + ES3_D3D11_FL10_1_WARP(), + ES3_D3D11_FL10_0_WARP(), + + // ES3 on top of D3D11 reference feature level 9.3 to 11.0 + ES3_D3D11_REFERENCE(), + ES3_D3D11_FL11_0_REFERENCE(), + ES3_D3D11_FL10_1_REFERENCE(), + ES3_D3D11_FL10_0_REFERENCE(), + + // ES2 on top of desktop OpenGL versions 2.1 to 4.5 + ES2_OPENGL(), + ES2_OPENGL(2, 1), + ES2_OPENGL(3, 0), + ES2_OPENGL(3, 1), + ES2_OPENGL(3, 2), + ES2_OPENGL(3, 3), + ES2_OPENGL(4, 0), + ES2_OPENGL(4, 1), + ES2_OPENGL(4, 2), + ES2_OPENGL(4, 3), + ES2_OPENGL(4, 4), + ES2_OPENGL(4, 5), + + // ES2 on top of desktop OpenGL versions 3.2 to 4.5 + ES3_OPENGL(), + ES3_OPENGL(3, 2), + ES3_OPENGL(3, 3), + ES3_OPENGL(4, 0), + ES3_OPENGL(4, 1), + ES3_OPENGL(4, 2), + ES3_OPENGL(4, 3), + ES3_OPENGL(4, 4), + ES3_OPENGL(4, 5), + + // ES2 on top of OpenGL ES 2.0 to 3.2 + ES2_OPENGLES(), + ES2_OPENGLES(2, 0), + ES2_OPENGLES(3, 0), + ES2_OPENGLES(3, 1), + ES2_OPENGLES(3, 2), + + // ES2 on top of OpenGL ES 3.0 to 3.2 + ES3_OPENGLES(), + ES3_OPENGLES(3, 0), + ES3_OPENGLES(3, 1), + ES3_OPENGLES(3, 2)); } diff --git a/gfx/angle/src/tests/gl_tests/SRGBTextureTest.cpp b/gfx/angle/src/tests/gl_tests/SRGBTextureTest.cpp index c0c761020d..8c30fc1617 100644 --- a/gfx/angle/src/tests/gl_tests/SRGBTextureTest.cpp +++ b/gfx/angle/src/tests/gl_tests/SRGBTextureTest.cpp @@ -144,6 +144,11 @@ TEST_P(SRGBTextureTest, SRGBARenderbuffer) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(SRGBTextureTest, ES2_D3D9(), ES2_D3D11(), ES3_D3D11(), ES2_OPENGL()); +ANGLE_INSTANTIATE_TEST(SRGBTextureTest, + ES2_D3D9(), + ES2_D3D11(), + ES3_D3D11(), + ES2_OPENGL(), + ES2_OPENGLES()); } // namespace diff --git a/gfx/angle/src/tests/gl_tests/SimpleOperationTest.cpp b/gfx/angle/src/tests/gl_tests/SimpleOperationTest.cpp index c6e991f81d..b11fb9fe92 100644 --- a/gfx/angle/src/tests/gl_tests/SimpleOperationTest.cpp +++ b/gfx/angle/src/tests/gl_tests/SimpleOperationTest.cpp @@ -199,8 +199,14 @@ TEST_P(SimpleOperationTest, BufferSubData) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST( - SimpleOperationTest, - ES2_D3D9(), ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(SimpleOperationTest, + ES2_D3D9(), + ES2_D3D11(EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE), + ES2_D3D11(EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE), + ES3_D3D11(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); } // namespace diff --git a/gfx/angle/src/tests/gl_tests/SixteenBppTextureTest.cpp b/gfx/angle/src/tests/gl_tests/SixteenBppTextureTest.cpp index 569c9d3254..24e8145cf2 100644 --- a/gfx/angle/src/tests/gl_tests/SixteenBppTextureTest.cpp +++ b/gfx/angle/src/tests/gl_tests/SixteenBppTextureTest.cpp @@ -298,6 +298,11 @@ TEST_P(SixteenBppTextureTest, RGBA4444Validation) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(SixteenBppTextureTest, ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3(), ES2_OPENGL()); +ANGLE_INSTANTIATE_TEST(SixteenBppTextureTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES2_OPENGLES()); } // namespace diff --git a/gfx/angle/src/tests/gl_tests/StateChangeTest.cpp b/gfx/angle/src/tests/gl_tests/StateChangeTest.cpp new file mode 100644 index 0000000000..7f032711d0 --- /dev/null +++ b/gfx/angle/src/tests/gl_tests/StateChangeTest.cpp @@ -0,0 +1,244 @@ +// +// Copyright 2015 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. +// +// StateChangeTest: +// Specifically designed for an ANGLE implementation of GL, these tests validate that +// ANGLE's dirty bits systems don't get confused by certain sequences of state changes. +// + +#include "test_utils/ANGLETest.h" + +using namespace angle; + +namespace +{ + +class StateChangeTest : public ANGLETest +{ + protected: + StateChangeTest() : mFramebuffer(0) + { + setWindowWidth(64); + setWindowHeight(64); + setConfigRedBits(8); + setConfigGreenBits(8); + setConfigBlueBits(8); + setConfigAlphaBits(8); + + // Enable the no error extension to avoid syncing the FBO state on validation. + setNoErrorEnabled(true); + } + + void SetUp() override + { + ANGLETest::SetUp(); + + glGenFramebuffers(1, &mFramebuffer); + + mTextures.resize(2, 0); + glGenTextures(2, mTextures.data()); + + ASSERT_GL_NO_ERROR(); + } + + void TearDown() override + { + if (mFramebuffer != 0) + { + glDeleteFramebuffers(1, &mFramebuffer); + mFramebuffer = 0; + } + + if (!mTextures.empty()) + { + glDeleteTextures(static_cast(mTextures.size()), mTextures.data()); + mTextures.clear(); + } + + ANGLETest::TearDown(); + } + + GLuint mFramebuffer; + std::vector mTextures; +}; + +class StateChangeTestES3 : public StateChangeTest +{ + protected: + StateChangeTestES3() {} +}; + +} // anonymous namespace + +// Ensure that CopyTexImage2D syncs framebuffer changes. +TEST_P(StateChangeTest, CopyTexImage2DSync) +{ + if (isAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + // TODO(geofflang): Fix on Linux AMD drivers (http://anglebug.com/1291) + std::cout << "Test disabled on AMD OpenGL." << std::endl; + return; + } + + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + + // Init first texture to red + glBindTexture(GL_TEXTURE_2D, mTextures[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); + glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); + + // Init second texture to green + glBindTexture(GL_TEXTURE_2D, mTextures[1]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); + + // Copy in the red texture to the green one. + // CopyTexImage should sync the framebuffer attachment change. + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 16, 16, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); + EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); + + ASSERT_GL_NO_ERROR(); +} + +// Ensure that CopyTexSubImage2D syncs framebuffer changes. +TEST_P(StateChangeTest, CopyTexSubImage2DSync) +{ + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + + // Init first texture to red + glBindTexture(GL_TEXTURE_2D, mTextures[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); + glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); + + // Init second texture to green + glBindTexture(GL_TEXTURE_2D, mTextures[1]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); + + // Copy in the red texture to the green one. + // CopyTexImage should sync the framebuffer attachment change. + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 16, 16); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); + EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); + + ASSERT_GL_NO_ERROR(); +} + +// Ensure that CopyTexSubImage3D syncs framebuffer changes. +TEST_P(StateChangeTestES3, CopyTexSubImage3DSync) +{ + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + + // Init first texture to red + glBindTexture(GL_TEXTURE_3D, mTextures[0]); + glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 16, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[0], 0, 0); + glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); + + // Init second texture to green + glBindTexture(GL_TEXTURE_3D, mTextures[1]); + glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 16, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[1], 0, 0); + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); + + // Copy in the red texture to the green one. + // CopyTexImage should sync the framebuffer attachment change. + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[0], 0, 0); + glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 0, 0, 16, 16); + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[1], 0, 0); + EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); + + ASSERT_GL_NO_ERROR(); +} + +// Ensure that BlitFramebuffer syncs framebuffer changes. +TEST_P(StateChangeTestES3, BlitFramebufferSync) +{ + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + + // Init first texture to red + glBindTexture(GL_TEXTURE_2D, mTextures[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); + glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); + + // Init second texture to green + glBindTexture(GL_TEXTURE_2D, mTextures[1]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); + + // Change to the red textures and blit. + // BlitFramebuffer should sync the framebuffer attachment change. + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], + 0); + glBlitFramebuffer(0, 0, 16, 16, 0, 0, 16, 16, GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); + + ASSERT_GL_NO_ERROR(); +} + +// Ensure that ReadBuffer and DrawBuffers sync framebuffer changes. +TEST_P(StateChangeTestES3, ReadBufferAndDrawBuffersSync) +{ + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + + // Initialize two FBO attachments + glBindTexture(GL_TEXTURE_2D, mTextures[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); + glBindTexture(GL_TEXTURE_2D, mTextures[1]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, mTextures[1], 0); + + // Clear first attachment to red + GLenum bufs1[] = {GL_COLOR_ATTACHMENT0, GL_NONE}; + glDrawBuffers(2, bufs1); + glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + // Clear second texture to green + GLenum bufs2[] = {GL_NONE, GL_COLOR_ATTACHMENT1}; + glDrawBuffers(2, bufs2); + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + // Verify first attachment is red and second is green + glReadBuffer(GL_COLOR_ATTACHMENT1); + EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); + + glReadBuffer(GL_COLOR_ATTACHMENT0); + EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); + + ASSERT_GL_NO_ERROR(); +} + +ANGLE_INSTANTIATE_TEST(StateChangeTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL()); +ANGLE_INSTANTIATE_TEST(StateChangeTestES3, ES3_D3D11(), ES3_OPENGL()); diff --git a/gfx/angle/src/tests/gl_tests/SwizzleTest.cpp b/gfx/angle/src/tests/gl_tests/SwizzleTest.cpp index 6eb2d1c809..a91030cb6e 100644 --- a/gfx/angle/src/tests/gl_tests/SwizzleTest.cpp +++ b/gfx/angle/src/tests/gl_tests/SwizzleTest.cpp @@ -340,6 +340,6 @@ TEST_P(SwizzleTest, CompressedDXT_2D) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(SwizzleTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGL(3, 3)); +ANGLE_INSTANTIATE_TEST(SwizzleTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGL(3, 3), ES3_OPENGLES()); } // namespace diff --git a/gfx/angle/src/tests/gl_tests/TextureTest.cpp b/gfx/angle/src/tests/gl_tests/TextureTest.cpp index c744529db4..261ff743b4 100644 --- a/gfx/angle/src/tests/gl_tests/TextureTest.cpp +++ b/gfx/angle/src/tests/gl_tests/TextureTest.cpp @@ -11,10 +11,10 @@ using namespace angle; namespace { -class TextureTest : public ANGLETest +class TexCoordDrawTest : public ANGLETest { protected: - TextureTest() + TexCoordDrawTest() : ANGLETest(), mProgram(0), mFramebuffer(0), mFramebufferColorTexture(0) { setWindowWidth(128); setWindowHeight(128); @@ -24,38 +24,96 @@ class TextureTest : public ANGLETest setConfigAlphaBits(8); } - void SetUp() override + virtual std::string getVertexShaderSource() { - ANGLETest::SetUp(); - glGenTextures(1, &mTexture2D); - glGenTextures(1, &mTextureCube); - - glBindTexture(GL_TEXTURE_2D, mTexture2D); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - EXPECT_GL_NO_ERROR(); - - glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube); - glTexStorage2DEXT(GL_TEXTURE_CUBE_MAP, 1, GL_RGBA8, 1, 1); - EXPECT_GL_NO_ERROR(); - - ASSERT_GL_NO_ERROR(); - - const std::string vertexShaderSource = SHADER_SOURCE + return std::string(SHADER_SOURCE ( precision highp float; attribute vec4 position; varying vec2 texcoord; - uniform vec2 textureScale; - void main() { - gl_Position = vec4(position.xy * textureScale, 0.0, 1.0); + gl_Position = vec4(position.xy, 0.0, 1.0); texcoord = (position.xy * 0.5) + 0.5; } + ) ); + } - const std::string fragmentShaderSource2D = SHADER_SOURCE + virtual std::string getFragmentShaderSource() = 0; + + void SetUp() override + { + ANGLETest::SetUp(); + const std::string vertexShaderSource = getVertexShaderSource(); + const std::string fragmentShaderSource = getFragmentShaderSource(); + + mProgram = CompileProgram(vertexShaderSource, fragmentShaderSource); + ASSERT_NE(0u, mProgram); + ASSERT_GL_NO_ERROR(); + + setUpFramebuffer(); + } + + void TearDown() override + { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &mFramebuffer); + glDeleteTextures(1, &mFramebufferColorTexture); + glDeleteProgram(mProgram); + ANGLETest::TearDown(); + } + + void setUpFramebuffer() + { + // We use an FBO to work around an issue where the default framebuffer applies SRGB + // conversion (particularly known to happen incorrectly on Intel GL drivers). It's not + // clear whether this issue can even be fixed on all backends. For example GLES 3.0.4 spec + // section 4.4 says that the format of the default framebuffer is entirely up to the window + // system, so it might be SRGB, and GLES 3.0 doesn't have a "FRAMEBUFFER_SRGB" to turn off + // SRGB conversion like desktop GL does. + // TODO(oetuaho): Get rid of this if the underlying issue is fixed. + glGenFramebuffers(1, &mFramebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + + glGenTextures(1, &mFramebufferColorTexture); + glBindTexture(GL_TEXTURE_2D, mFramebufferColorTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + mFramebufferColorTexture, 0); + ASSERT_GL_NO_ERROR(); + ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); + glBindTexture(GL_TEXTURE_2D, 0); + } + + // Returns the created texture ID. + GLuint create2DTexture() + { + GLuint texture2D; + glGenTextures(1, &texture2D); + glBindTexture(GL_TEXTURE_2D, texture2D); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + EXPECT_GL_NO_ERROR(); + return texture2D; + } + + GLuint mProgram; + GLuint mFramebuffer; + + private: + GLuint mFramebufferColorTexture; +}; + +class Texture2DTest : public TexCoordDrawTest +{ + protected: + Texture2DTest() : TexCoordDrawTest(), mTexture2D(0), mTexture2DUniformLocation(-1) {} + + std::string getFragmentShaderSource() override + { + return std::string(SHADER_SOURCE ( precision highp float; uniform sampler2D tex; @@ -65,47 +123,25 @@ class TextureTest : public ANGLETest { gl_FragColor = texture2D(tex, texcoord); } + ) ); + } - const std::string fragmentShaderSourceCube = SHADER_SOURCE - ( - precision highp float; - uniform sampler2D tex2D; - uniform samplerCube texCube; - varying vec2 texcoord; + void SetUp() override + { + TexCoordDrawTest::SetUp(); + mTexture2D = create2DTexture(); - void main() - { - gl_FragColor = texture2D(tex2D, texcoord); - gl_FragColor += textureCube(texCube, vec3(texcoord, 0)); - } - ); - - m2DProgram = CompileProgram(vertexShaderSource, fragmentShaderSource2D); - mCubeProgram = CompileProgram(vertexShaderSource, fragmentShaderSourceCube); - ASSERT_NE(0u, m2DProgram); - ASSERT_NE(0u, mCubeProgram); - - mTexture2DUniformLocation = glGetUniformLocation(m2DProgram, "tex"); - ASSERT_NE(-1, mTexture2DUniformLocation); - - mTextureScaleUniformLocation = glGetUniformLocation(m2DProgram, "textureScale"); - ASSERT_NE(-1, mTextureScaleUniformLocation); - - glUseProgram(m2DProgram); - glUniform2f(mTextureScaleUniformLocation, 1.0f, 1.0f); - glUseProgram(0); ASSERT_GL_NO_ERROR(); + + mTexture2DUniformLocation = glGetUniformLocation(mProgram, "tex"); + ASSERT_NE(-1, mTexture2DUniformLocation); } void TearDown() override { glDeleteTextures(1, &mTexture2D); - glDeleteTextures(1, &mTextureCube); - glDeleteProgram(m2DProgram); - glDeleteProgram(mCubeProgram); - - ANGLETest::TearDown(); + TexCoordDrawTest::TearDown(); } // Tests CopyTexSubImage with floating point textures of various formats. @@ -216,7 +252,7 @@ class TextureTest : public ANGLETest ASSERT_GL_NO_ERROR(); glBindFramebuffer(GL_FRAMEBUFFER, 0); - drawQuad(m2DProgram, "position", 0.5f); + drawQuad(mProgram, "position", 0.5f); int testImageChannels = std::min(sourceImageChannels, destImageChannels); @@ -238,58 +274,314 @@ class TextureTest : public ANGLETest } GLuint mTexture2D; - GLuint mTextureCube; - - GLuint m2DProgram; - GLuint mCubeProgram; GLint mTexture2DUniformLocation; - GLint mTextureScaleUniformLocation; }; -class TextureTestES3 : public ANGLETest +class Texture2DTestES3 : public Texture2DTest { protected: - TextureTestES3() - : m2DArrayTexture(0), - m2DArrayProgram(0), - mTextureArrayLocation(-1) + Texture2DTestES3() : Texture2DTest() {} + + std::string getVertexShaderSource() override { - setWindowWidth(128); - setWindowHeight(128); - setConfigRedBits(8); - setConfigGreenBits(8); - setConfigBlueBits(8); - setConfigAlphaBits(8); + return std::string( + "#version 300 es\n" + "out vec2 texcoord;\n" + "in vec4 position;\n" + "void main()\n" + "{\n" + " gl_Position = vec4(position.xy, 0.0, 1.0);\n" + " texcoord = (position.xy * 0.5) + 0.5;\n" + "}\n"); + } + + std::string getFragmentShaderSource() override + { + return std::string( + "#version 300 es\n" + "precision highp float;\n" + "uniform highp sampler2D tex;\n" + "in vec2 texcoord;\n" + "out vec4 fragColor;\n" + "void main()\n" + "{\n" + " fragColor = texture(tex, texcoord);\n" + "}\n"); + } +}; + +class Texture2DTestWithDrawScale : public Texture2DTest +{ + protected: + Texture2DTestWithDrawScale() : Texture2DTest(), mDrawScaleUniformLocation(-1) {} + + std::string getVertexShaderSource() override + { + return std::string(SHADER_SOURCE + ( + precision highp float; + attribute vec4 position; + varying vec2 texcoord; + + uniform vec2 drawScale; + + void main() + { + gl_Position = vec4(position.xy * drawScale, 0.0, 1.0); + texcoord = (position.xy * 0.5) + 0.5; + } + ) + ); } void SetUp() override { - ANGLETest::SetUp(); + Texture2DTest::SetUp(); + mDrawScaleUniformLocation = glGetUniformLocation(mProgram, "drawScale"); + ASSERT_NE(-1, mDrawScaleUniformLocation); - const std::string vertexShaderSource = + glUseProgram(mProgram); + glUniform2f(mDrawScaleUniformLocation, 1.0f, 1.0f); + glUseProgram(0); + ASSERT_GL_NO_ERROR(); + } + + GLint mDrawScaleUniformLocation; +}; + +class Sampler2DAsFunctionParameterTest : public Texture2DTest +{ + protected: + Sampler2DAsFunctionParameterTest() : Texture2DTest() {} + + std::string getFragmentShaderSource() override + { + return std::string(SHADER_SOURCE + ( + precision highp float; + uniform sampler2D tex; + varying vec2 texcoord; + + vec4 computeFragColor(sampler2D aTex) + { + return texture2D(aTex, texcoord); + } + + void main() + { + gl_FragColor = computeFragColor(tex); + } + ) + ); + } +}; + +class TextureCubeTest : public TexCoordDrawTest +{ + protected: + TextureCubeTest() + : TexCoordDrawTest(), + mTexture2D(0), + mTextureCube(0), + mTexture2DUniformLocation(-1), + mTextureCubeUniformLocation(-1) + { + } + + std::string getFragmentShaderSource() override + { + return std::string(SHADER_SOURCE + ( + precision highp float; + uniform sampler2D tex2D; + uniform samplerCube texCube; + varying vec2 texcoord; + + void main() + { + gl_FragColor = texture2D(tex2D, texcoord); + gl_FragColor += textureCube(texCube, vec3(texcoord, 0)); + } + ) + ); + } + + void SetUp() override + { + TexCoordDrawTest::SetUp(); + + glGenTextures(1, &mTextureCube); + glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube); + glTexStorage2DEXT(GL_TEXTURE_CUBE_MAP, 1, GL_RGBA8, 1, 1); + EXPECT_GL_NO_ERROR(); + + mTexture2D = create2DTexture(); + + mTexture2DUniformLocation = glGetUniformLocation(mProgram, "tex2D"); + ASSERT_NE(-1, mTexture2DUniformLocation); + mTextureCubeUniformLocation = glGetUniformLocation(mProgram, "texCube"); + ASSERT_NE(-1, mTextureCubeUniformLocation); + } + + void TearDown() override + { + glDeleteTextures(1, &mTextureCube); + TexCoordDrawTest::TearDown(); + } + + GLuint mTexture2D; + GLuint mTextureCube; + GLint mTexture2DUniformLocation; + GLint mTextureCubeUniformLocation; +}; + +class SamplerArrayTest : public TexCoordDrawTest +{ + protected: + SamplerArrayTest() + : TexCoordDrawTest(), + mTexture2DA(0), + mTexture2DB(0), + mTexture0UniformLocation(-1), + mTexture1UniformLocation(-1) + { + } + + std::string getFragmentShaderSource() override + { + return std::string(SHADER_SOURCE + ( + precision mediump float; + uniform highp sampler2D tex2DArray[2]; + varying vec2 texcoord; + void main() + { + gl_FragColor = texture2D(tex2DArray[0], texcoord); + gl_FragColor += texture2D(tex2DArray[1], texcoord); + } + ) + ); + } + + void SetUp() override + { + TexCoordDrawTest::SetUp(); + + mTexture0UniformLocation = glGetUniformLocation(mProgram, "tex2DArray[0]"); + ASSERT_NE(-1, mTexture0UniformLocation); + mTexture1UniformLocation = glGetUniformLocation(mProgram, "tex2DArray[1]"); + ASSERT_NE(-1, mTexture1UniformLocation); + + mTexture2DA = create2DTexture(); + mTexture2DB = create2DTexture(); + ASSERT_GL_NO_ERROR(); + } + + void TearDown() override + { + glDeleteTextures(1, &mTexture2DA); + glDeleteTextures(1, &mTexture2DB); + TexCoordDrawTest::TearDown(); + } + + void testSamplerArrayDraw() + { + GLubyte texData[4]; + texData[0] = 0; + texData[1] = 60; + texData[2] = 0; + texData[3] = 255; + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mTexture2DA); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, texData); + + texData[1] = 120; + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, mTexture2DB); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, texData); + EXPECT_GL_ERROR(GL_NO_ERROR); + + glUseProgram(mProgram); + glUniform1i(mTexture0UniformLocation, 0); + glUniform1i(mTexture1UniformLocation, 1); + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + + EXPECT_PIXEL_NEAR(0, 0, 0, 180, 0, 255, 2); + } + + GLuint mTexture2DA; + GLuint mTexture2DB; + GLint mTexture0UniformLocation; + GLint mTexture1UniformLocation; +}; + + +class SamplerArrayAsFunctionParameterTest : public SamplerArrayTest +{ + protected: + SamplerArrayAsFunctionParameterTest() : SamplerArrayTest() {} + + std::string getFragmentShaderSource() override + { + return std::string(SHADER_SOURCE + ( + precision mediump float; + uniform highp sampler2D tex2DArray[2]; + varying vec2 texcoord; + + vec4 computeFragColor(highp sampler2D aTex2DArray[2]) + { + return texture2D(aTex2DArray[0], texcoord) + texture2D(aTex2DArray[1], texcoord); + } + + void main() + { + gl_FragColor = computeFragColor(tex2DArray); + } + ) + ); + } +}; + +class Texture2DArrayTestES3 : public TexCoordDrawTest +{ + protected: + Texture2DArrayTestES3() : TexCoordDrawTest(), m2DArrayTexture(0), mTextureArrayLocation(-1) {} + + std::string getVertexShaderSource() override + { + return std::string( "#version 300 es\n" "out vec2 texcoord;\n" "in vec4 position;\n" - "void main() {\n" - " gl_Position = vec4(position.xy, 0.0, 1.0);\n" - " texcoord = (position.xy * 0.5) + 0.5;\n" - "}"; + "void main()\n" + "{\n" + " gl_Position = vec4(position.xy, 0.0, 1.0);\n" + " texcoord = (position.xy * 0.5) + 0.5;\n" + "}\n"); + } - const std::string fragmentShaderSource2DArray = + std::string getFragmentShaderSource() override + { + return std::string( "#version 300 es\n" "precision highp float;\n" - "uniform sampler2DArray tex2DArray;\n" + "uniform highp sampler2DArray tex2DArray;\n" "in vec2 texcoord;\n" "out vec4 fragColor;\n" "void main()\n" "{\n" " fragColor = texture(tex2DArray, vec3(texcoord.x, texcoord.y, 0.0));\n" - "}\n"; + "}\n"); + } - m2DArrayProgram = CompileProgram(vertexShaderSource, fragmentShaderSource2DArray); - ASSERT_NE(0u, m2DArrayProgram); + void SetUp() override + { + TexCoordDrawTest::SetUp(); - mTextureArrayLocation = glGetUniformLocation(m2DArrayProgram, "tex2DArray"); + mTextureArrayLocation = glGetUniformLocation(mProgram, "tex2DArray"); ASSERT_NE(-1, mTextureArrayLocation); glGenTextures(1, &m2DArrayTexture); @@ -299,15 +591,190 @@ class TextureTestES3 : public ANGLETest void TearDown() override { glDeleteTextures(1, &m2DArrayTexture); - glDeleteProgram(m2DArrayProgram); + TexCoordDrawTest::TearDown(); } GLuint m2DArrayTexture; - GLuint m2DArrayProgram; GLint mTextureArrayLocation; }; -TEST_P(TextureTest, NegativeAPISubImage) +class ShadowSamplerPlusSampler3DTestES3 : public TexCoordDrawTest +{ + protected: + ShadowSamplerPlusSampler3DTestES3() + : TexCoordDrawTest(), + mTextureShadow(0), + mTexture3D(0), + mTextureShadowUniformLocation(-1), + mTexture3DUniformLocation(-1), + mDepthRefUniformLocation(-1) + { + } + + std::string getVertexShaderSource() override + { + return std::string( + "#version 300 es\n" + "out vec2 texcoord;\n" + "in vec4 position;\n" + "void main()\n" + "{\n" + " gl_Position = vec4(position.xy, 0.0, 1.0);\n" + " texcoord = (position.xy * 0.5) + 0.5;\n" + "}\n"); + } + + std::string getFragmentShaderSource() override + { + return std::string( + "#version 300 es\n" + "precision highp float;\n" + "uniform highp sampler2DShadow tex2DShadow;\n" + "uniform highp sampler3D tex3D;\n" + "in vec2 texcoord;\n" + "uniform float depthRef;\n" + "out vec4 fragColor;\n" + "void main()\n" + "{\n" + " fragColor = vec4(texture(tex2DShadow, vec3(texcoord, depthRef)) * 0.5);\n" + " fragColor += texture(tex3D, vec3(texcoord, 0.0));\n" + "}\n"); + } + + void SetUp() override + { + TexCoordDrawTest::SetUp(); + + glGenTextures(1, &mTexture3D); + + glGenTextures(1, &mTextureShadow); + glBindTexture(GL_TEXTURE_2D, mTextureShadow); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + + mTextureShadowUniformLocation = glGetUniformLocation(mProgram, "tex2DShadow"); + ASSERT_NE(-1, mTextureShadowUniformLocation); + mTexture3DUniformLocation = glGetUniformLocation(mProgram, "tex3D"); + ASSERT_NE(-1, mTexture3DUniformLocation); + mDepthRefUniformLocation = glGetUniformLocation(mProgram, "depthRef"); + ASSERT_NE(-1, mDepthRefUniformLocation); + } + + void TearDown() override + { + glDeleteTextures(1, &mTextureShadow); + glDeleteTextures(1, &mTexture3D); + TexCoordDrawTest::TearDown(); + } + + GLuint mTextureShadow; + GLuint mTexture3D; + GLint mTextureShadowUniformLocation; + GLint mTexture3DUniformLocation; + GLint mDepthRefUniformLocation; +}; + +class SamplerTypeMixTestES3 : public TexCoordDrawTest +{ + protected: + SamplerTypeMixTestES3() + : TexCoordDrawTest(), + mTexture2D(0), + mTextureCube(0), + mTexture2DShadow(0), + mTextureCubeShadow(0), + mTexture2DUniformLocation(-1), + mTextureCubeUniformLocation(-1), + mTexture2DShadowUniformLocation(-1), + mTextureCubeShadowUniformLocation(-1), + mDepthRefUniformLocation(-1) + { + } + + std::string getVertexShaderSource() override + { + return std::string( + "#version 300 es\n" + "out vec2 texcoord;\n" + "in vec4 position;\n" + "void main()\n" + "{\n" + " gl_Position = vec4(position.xy, 0.0, 1.0);\n" + " texcoord = (position.xy * 0.5) + 0.5;\n" + "}\n"); + } + + std::string getFragmentShaderSource() override + { + return std::string( + "#version 300 es\n" + "precision highp float;\n" + "uniform highp sampler2D tex2D;\n" + "uniform highp samplerCube texCube;\n" + "uniform highp sampler2DShadow tex2DShadow;\n" + "uniform highp samplerCubeShadow texCubeShadow;\n" + "in vec2 texcoord;\n" + "uniform float depthRef;\n" + "out vec4 fragColor;\n" + "void main()\n" + "{\n" + " fragColor = texture(tex2D, texcoord);\n" + " fragColor += texture(texCube, vec3(1.0, 0.0, 0.0));\n" + " fragColor += vec4(texture(tex2DShadow, vec3(texcoord, depthRef)) * 0.25);\n" + " fragColor += vec4(texture(texCubeShadow, vec4(1.0, 0.0, 0.0, depthRef)) * " + "0.125);\n" + "}\n"); + } + + void SetUp() override + { + TexCoordDrawTest::SetUp(); + + glGenTextures(1, &mTexture2D); + glGenTextures(1, &mTextureCube); + + glGenTextures(1, &mTexture2DShadow); + glBindTexture(GL_TEXTURE_2D, mTexture2DShadow); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + + glGenTextures(1, &mTextureCubeShadow); + glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCubeShadow); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + + mTexture2DUniformLocation = glGetUniformLocation(mProgram, "tex2D"); + ASSERT_NE(-1, mTexture2DUniformLocation); + mTextureCubeUniformLocation = glGetUniformLocation(mProgram, "texCube"); + ASSERT_NE(-1, mTextureCubeUniformLocation); + mTexture2DShadowUniformLocation = glGetUniformLocation(mProgram, "tex2DShadow"); + ASSERT_NE(-1, mTexture2DShadowUniformLocation); + mTextureCubeShadowUniformLocation = glGetUniformLocation(mProgram, "texCubeShadow"); + ASSERT_NE(-1, mTextureCubeShadowUniformLocation); + mDepthRefUniformLocation = glGetUniformLocation(mProgram, "depthRef"); + ASSERT_NE(-1, mDepthRefUniformLocation); + + ASSERT_GL_NO_ERROR(); + } + + void TearDown() override + { + glDeleteTextures(1, &mTexture2D); + glDeleteTextures(1, &mTextureCube); + glDeleteTextures(1, &mTexture2DShadow); + glDeleteTextures(1, &mTextureCubeShadow); + TexCoordDrawTest::TearDown(); + } + + GLuint mTexture2D; + GLuint mTextureCube; + GLuint mTexture2DShadow; + GLuint mTextureCubeShadow; + GLint mTexture2DUniformLocation; + GLint mTextureCubeUniformLocation; + GLint mTexture2DShadowUniformLocation; + GLint mTextureCubeShadowUniformLocation; + GLint mDepthRefUniformLocation; +}; + +TEST_P(Texture2DTest, NegativeAPISubImage) { glBindTexture(GL_TEXTURE_2D, mTexture2D); EXPECT_GL_ERROR(GL_NO_ERROR); @@ -317,15 +784,15 @@ TEST_P(TextureTest, NegativeAPISubImage) EXPECT_GL_ERROR(GL_INVALID_VALUE); } -TEST_P(TextureTest, ZeroSizedUploads) +TEST_P(Texture2DTest, ZeroSizedUploads) { glBindTexture(GL_TEXTURE_2D, mTexture2D); EXPECT_GL_ERROR(GL_NO_ERROR); // Use the texture first to make sure it's in video memory - glUseProgram(m2DProgram); + glUseProgram(mProgram); glUniform1i(mTexture2DUniformLocation, 0); - drawQuad(m2DProgram, "position", 0.5f); + drawQuad(mProgram, "position", 0.5f); const GLubyte *pixel[4] = { 0 }; @@ -340,7 +807,7 @@ TEST_P(TextureTest, ZeroSizedUploads) } // Test drawing with two texture types, to trigger an ANGLE bug in validation -TEST_P(TextureTest, CubeMapBug) +TEST_P(TextureCubeTest, CubeMapBug) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, mTexture2D); @@ -348,19 +815,78 @@ TEST_P(TextureTest, CubeMapBug) glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube); EXPECT_GL_ERROR(GL_NO_ERROR); - glUseProgram(mCubeProgram); - GLint tex2DUniformLocation = glGetUniformLocation(mCubeProgram, "tex2D"); - GLint texCubeUniformLocation = glGetUniformLocation(mCubeProgram, "texCube"); - EXPECT_NE(-1, tex2DUniformLocation); - EXPECT_NE(-1, texCubeUniformLocation); - glUniform1i(tex2DUniformLocation, 0); - glUniform1i(texCubeUniformLocation, 1); - drawQuad(mCubeProgram, "position", 0.5f); + glUseProgram(mProgram); + glUniform1i(mTexture2DUniformLocation, 0); + glUniform1i(mTextureCubeUniformLocation, 1); + drawQuad(mProgram, "position", 0.5f); EXPECT_GL_NO_ERROR(); } +// Test drawing with two texture types accessed from the same shader and check that the result of +// drawing is correct. +TEST_P(TextureCubeTest, CubeMapDraw) +{ + GLubyte texData[4]; + texData[0] = 0; + texData[1] = 60; + texData[2] = 0; + texData[3] = 255; + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mTexture2D); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, texData); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube); + texData[1] = 120; + glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + texData); + EXPECT_GL_ERROR(GL_NO_ERROR); + + glUseProgram(mProgram); + glUniform1i(mTexture2DUniformLocation, 0); + glUniform1i(mTextureCubeUniformLocation, 1); + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + + int px = getWindowWidth() - 1; + int py = 0; + EXPECT_PIXEL_NEAR(px, py, 0, 180, 0, 255, 2); +} + +TEST_P(Sampler2DAsFunctionParameterTest, Sampler2DAsFunctionParameter) +{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mTexture2D); + GLubyte texData[4]; + texData[0] = 0; + texData[1] = 128; + texData[2] = 0; + texData[3] = 255; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, texData); + glUseProgram(mProgram); + glUniform1i(mTexture2DUniformLocation, 0); + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + + EXPECT_PIXEL_NEAR(0, 0, 0, 128, 0, 255, 2); +} + +// Test drawing with two textures passed to the shader in a sampler array. +TEST_P(SamplerArrayTest, SamplerArrayDraw) +{ + testSamplerArrayDraw(); +} + +// Test drawing with two textures passed to the shader in a sampler array which is passed to a +// user-defined function in the shader. +TEST_P(SamplerArrayAsFunctionParameterTest, SamplerArrayAsFunctionParameter) +{ + testSamplerArrayDraw(); +} + // Copy of a test in conformance/textures/texture-mips, to test generate mipmaps -TEST_P(TextureTest, MipmapsTwice) +TEST_P(Texture2DTestWithDrawScale, MipmapsTwice) { int px = getWindowWidth() / 2; int py = getWindowHeight() / 2; @@ -383,10 +909,10 @@ TEST_P(TextureTest, MipmapsTwice) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glGenerateMipmap(GL_TEXTURE_2D); - glUseProgram(m2DProgram); + glUseProgram(mProgram); glUniform1i(mTexture2DUniformLocation, 0); - glUniform2f(mTextureScaleUniformLocation, 0.0625f, 0.0625f); - drawQuad(m2DProgram, "position", 0.5f); + glUniform2f(mDrawScaleUniformLocation, 0.0625f, 0.0625f); + drawQuad(mProgram, "position", 0.5f); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_EQ(px, py, 255, 0, 0, 255); @@ -414,7 +940,7 @@ TEST_P(TextureTest, MipmapsTwice) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); glGenerateMipmap(GL_TEXTURE_2D); - drawQuad(m2DProgram, "position", 0.5f); + drawQuad(mProgram, "position", 0.5f); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_EQ(px, py, 0, 255, 0, 255); @@ -422,7 +948,7 @@ TEST_P(TextureTest, MipmapsTwice) // Test creating a FBO with a cube map render target, to test an ANGLE bug // https://code.google.com/p/angleproject/issues/detail?id=849 -TEST_P(TextureTest, CubeMapFBO) +TEST_P(TextureCubeTest, CubeMapFBO) { GLuint fbo; glGenFramebuffers(1, &fbo); @@ -439,7 +965,7 @@ TEST_P(TextureTest, CubeMapFBO) } // Test that glTexSubImage2D works properly when glTexStorage2DEXT has initialized the image with a default color. -TEST_P(TextureTest, TexStorage) +TEST_P(Texture2DTest, TexStorage) { int width = getWindowWidth(); int height = getWindowHeight(); @@ -469,10 +995,9 @@ TEST_P(TextureTest, TexStorage) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glUseProgram(m2DProgram); + glUseProgram(mProgram); glUniform1i(mTexture2DUniformLocation, 0); - glUniform2f(mTextureScaleUniformLocation, 1.f, 1.f); - drawQuad(m2DProgram, "position", 0.5f); + drawQuad(mProgram, "position", 0.5f); glDeleteTextures(1, &tex2D); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_EQ(width / 4, height / 4, 255, 0, 0, 255); @@ -484,7 +1009,7 @@ TEST_P(TextureTest, TexStorage) } // Test that glTexSubImage2D combined with a PBO works properly when glTexStorage2DEXT has initialized the image with a default color. -TEST_P(TextureTest, TexStorageWithPBO) +TEST_P(Texture2DTest, TexStorageWithPBO) { if (extensionEnabled("NV_pixel_buffer_object")) { @@ -522,12 +1047,11 @@ TEST_P(TextureTest, TexStorageWithPBO) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glUseProgram(m2DProgram); + glUseProgram(mProgram); glUniform1i(mTexture2DUniformLocation, 0); - glUniform2f(mTextureScaleUniformLocation, 1.f, 1.f); - drawQuad(m2DProgram, "position", 0.5f); + drawQuad(mProgram, "position", 0.5f); glDeleteTextures(1, &tex2D); - glDeleteTextures(1, &pbo); + glDeleteBuffers(1, &pbo); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_EQ(3 * width / 4, 3 * height / 4, 0, 0, 0, 255); EXPECT_PIXEL_EQ(width / 4, height / 4, 255, 0, 0, 255); @@ -535,59 +1059,80 @@ TEST_P(TextureTest, TexStorageWithPBO) } // See description on testFloatCopySubImage -TEST_P(TextureTest, CopySubImageFloat_R_R) +TEST_P(Texture2DTest, CopySubImageFloat_R_R) { testFloatCopySubImage(1, 1); } -TEST_P(TextureTest, CopySubImageFloat_RG_R) +TEST_P(Texture2DTest, CopySubImageFloat_RG_R) { testFloatCopySubImage(2, 1); } -TEST_P(TextureTest, CopySubImageFloat_RG_RG) +TEST_P(Texture2DTest, CopySubImageFloat_RG_RG) { testFloatCopySubImage(2, 2); } -TEST_P(TextureTest, CopySubImageFloat_RGB_R) +TEST_P(Texture2DTest, CopySubImageFloat_RGB_R) { testFloatCopySubImage(3, 1); } -TEST_P(TextureTest, CopySubImageFloat_RGB_RG) +TEST_P(Texture2DTest, CopySubImageFloat_RGB_RG) { testFloatCopySubImage(3, 2); } -TEST_P(TextureTest, CopySubImageFloat_RGB_RGB) +TEST_P(Texture2DTest, CopySubImageFloat_RGB_RGB) { + // TODO (bug 1284): Investigate RGBA32f D3D SDK Layers messages on D3D11_FL9_3 + if (isD3D11_FL93()) + { + std::cout << "Test skipped on Feature Level 9_3." << std::endl; + return; + } + testFloatCopySubImage(3, 3); } -TEST_P(TextureTest, CopySubImageFloat_RGBA_R) +TEST_P(Texture2DTest, CopySubImageFloat_RGBA_R) { testFloatCopySubImage(4, 1); } -TEST_P(TextureTest, CopySubImageFloat_RGBA_RG) +TEST_P(Texture2DTest, CopySubImageFloat_RGBA_RG) { testFloatCopySubImage(4, 2); } -TEST_P(TextureTest, CopySubImageFloat_RGBA_RGB) +TEST_P(Texture2DTest, CopySubImageFloat_RGBA_RGB) { + // TODO (bug 1284): Investigate RGBA32f D3D SDK Layers messages on D3D11_FL9_3 + if (isD3D11_FL93()) + { + std::cout << "Test skipped on Feature Level 9_3." << std::endl; + return; + } + testFloatCopySubImage(4, 3); } -TEST_P(TextureTest, CopySubImageFloat_RGBA_RGBA) +TEST_P(Texture2DTest, CopySubImageFloat_RGBA_RGBA) { + // TODO (bug 1284): Investigate RGBA32f D3D SDK Layers messages on D3D11_FL9_3 + if (isD3D11_FL93()) + { + std::cout << "Test skipped on Feature Level 9_3." << std::endl; + return; + } + testFloatCopySubImage(4, 4); } // Port of https://www.khronos.org/registry/webgl/conformance-suites/1.0.3/conformance/textures/texture-npot.html // Run against GL_ALPHA/UNSIGNED_BYTE format, to ensure that D3D11 Feature Level 9_3 correctly handles GL_ALPHA -TEST_P(TextureTest, TextureNPOT_GL_ALPHA_UBYTE) +TEST_P(Texture2DTest, TextureNPOT_GL_ALPHA_UBYTE) { const int npotTexSize = 5; const int potTexSize = 4; // Should be less than npotTexSize @@ -601,6 +1146,9 @@ TEST_P(TextureTest, TextureNPOT_GL_ALPHA_UBYTE) glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + // Default unpack alignment is 4. The values of 'pixels' below needs it to be 1. + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glActiveTexture(GL_TEXTURE0); glGenTextures(1, &tex2D); glBindTexture(GL_TEXTURE_2D, tex2D); @@ -632,7 +1180,7 @@ TEST_P(TextureTest, TextureNPOT_GL_ALPHA_UBYTE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glClear(GL_COLOR_BUFFER_BIT); - drawQuad(m2DProgram, "position", 1.0f); + drawQuad(mProgram, "position", 1.0f); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 255); // NPOT texture with TEXTURE_MIN_FILTER not NEAREST or LINEAR should draw with 0,0,0,255 @@ -640,13 +1188,13 @@ TEST_P(TextureTest, TextureNPOT_GL_ALPHA_UBYTE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); glClear(GL_COLOR_BUFFER_BIT); - drawQuad(m2DProgram, "position", 1.0f); + drawQuad(mProgram, "position", 1.0f); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 255); // NPOT texture with TEXTURE_MIN_FILTER set to LINEAR should draw glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glClear(GL_COLOR_BUFFER_BIT); - drawQuad(m2DProgram, "position", 1.0f); + drawQuad(mProgram, "position", 1.0f); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 64); // Check that glTexImage2D for POT texture succeeds @@ -663,15 +1211,24 @@ TEST_P(TextureTest, TextureNPOT_GL_ALPHA_UBYTE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glClear(GL_COLOR_BUFFER_BIT); - drawQuad(m2DProgram, "position", 1.0f); + drawQuad(mProgram, "position", 1.0f); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 64); EXPECT_GL_NO_ERROR(); } // Test to ensure that glTexSubImage2D always accepts data for non-power-of-two subregions. // ANGLE previously rejected this if GL_OES_texture_npot wasn't active, which is incorrect. -TEST_P(TextureTest, NPOTSubImageParameters) +TEST_P(Texture2DTest, NPOTSubImageParameters) { + // TODO(geofflang): Allow the GL backend to accept SubImage calls with a null data ptr. (bug + // 1278) + if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE || + getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE) + { + std::cout << "Test disabled on OpenGL." << std::endl; + return; + } + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, mTexture2D); @@ -688,10 +1245,46 @@ TEST_P(TextureTest, NPOTSubImageParameters) EXPECT_GL_NO_ERROR(); } +// Test to check that texture completeness is determined correctly when the texture base level is +// greater than 0, and also that level 0 is not sampled when base level is greater than 0. +TEST_P(Texture2DTestES3, DrawWithBaseLevel1) +{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mTexture2D); + GLubyte texDataRed[4u * 4u * 4u]; + for (size_t i = 0u; i < 4u * 4u; ++i) + { + texDataRed[i * 4u] = 255u; + texDataRed[i * 4u + 1u] = 0u; + texDataRed[i * 4u + 2u] = 0u; + texDataRed[i * 4u + 3u] = 255u; + } + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, texDataRed); + GLubyte texDataGreen[2u * 2u * 4u]; + for (size_t i = 0u; i < 2u * 2u; ++i) + { + texDataGreen[i * 4u] = 0u; + texDataGreen[i * 4u + 1u] = 255u; + texDataGreen[i * 4u + 2u] = 0u; + texDataGreen[i * 4u + 3u] = 255u; + } + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, texDataGreen); + glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texDataGreen); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1); + + EXPECT_GL_NO_ERROR(); + + drawQuad(mProgram, "position", 0.5f); + + EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); +} + // In the D3D11 renderer, we need to initialize some texture formats, to fill empty channels. EG RBA->RGBA8, with 1.0 // in the alpha channel. This test covers a bug where redefining array textures with these formats does not work as // expected. -TEST_P(TextureTestES3, RedefineInittableArray) +TEST_P(Texture2DArrayTestES3, RedefineInittableArray) { std::vector pixelData; for (size_t count = 0; count < 5000; count++) @@ -702,7 +1295,7 @@ TEST_P(TextureTestES3, RedefineInittableArray) } glBindTexture(GL_TEXTURE_2D_ARRAY, m2DArrayTexture); - glUseProgram(m2DArrayProgram); + glUseProgram(mProgram); glUniform1i(mTextureArrayLocation, 0); // The first draw worked correctly. @@ -712,17 +1305,121 @@ TEST_P(TextureTestES3, RedefineInittableArray) glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT); - drawQuad(m2DArrayProgram, "position", 1.0f); + drawQuad(mProgram, "position", 1.0f); EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); // The dimension of the respecification must match the original exactly to trigger the bug. glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGB, 4, 4, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, &pixelData[0]); - drawQuad(m2DArrayProgram, "position", 1.0f); + drawQuad(mProgram, "position", 1.0f); EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); ASSERT_GL_NO_ERROR(); } +// Test shadow sampler and regular non-shadow sampler coexisting in the same shader. +// This test is needed especially to confirm that sampler registers get assigned correctly on +// the HLSL backend even when there's a mix of different HLSL sampler and texture types. +TEST_P(ShadowSamplerPlusSampler3DTestES3, ShadowSamplerPlusSampler3DDraw) +{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_3D, mTexture3D); + GLubyte texData[4]; + texData[0] = 0; + texData[1] = 60; + texData[2] = 0; + texData[3] = 255; + glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, mTextureShadow); + GLfloat depthTexData[1]; + depthTexData[0] = 0.5f; + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, 1, 1, 0, GL_DEPTH_COMPONENT, GL_FLOAT, + depthTexData); + + glUseProgram(mProgram); + glUniform1f(mDepthRefUniformLocation, 0.3f); + glUniform1i(mTexture3DUniformLocation, 0); + glUniform1i(mTextureShadowUniformLocation, 1); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + // The shader writes 0.5 * + + EXPECT_PIXEL_NEAR(0, 0, 128, 188, 128, 255, 2); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_GREATER); + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + // The shader writes 0.5 * + + EXPECT_PIXEL_NEAR(0, 0, 0, 60, 0, 255, 2); +} + +// Test multiple different sampler types in the same shader. +// This test makes sure that even if sampler / texture registers get grouped together based on type +// or otherwise get shuffled around in the HLSL backend of the shader translator, the D3D renderer +// still has the right register index information for each ESSL sampler. +// The tested ESSL samplers have the following types in D3D11 HLSL: +// sampler2D: Texture2D + SamplerState +// samplerCube: TextureCube + SamplerState +// sampler2DShadow: Texture2D + SamplerComparisonState +// samplerCubeShadow: TextureCube + SamplerComparisonState +TEST_P(SamplerTypeMixTestES3, SamplerTypeMixDraw) +{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mTexture2D); + GLubyte texData[4]; + texData[0] = 0; + texData[1] = 0; + texData[2] = 120; + texData[3] = 255; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube); + texData[0] = 0; + texData[1] = 90; + texData[2] = 0; + texData[3] = 255; + glTexStorage2D(GL_TEXTURE_CUBE_MAP, 1, GL_RGBA8, 1, 1); + glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + texData); + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, mTexture2DShadow); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + GLfloat depthTexData[1]; + depthTexData[0] = 0.5f; + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, 1, 1, 0, GL_DEPTH_COMPONENT, GL_FLOAT, + depthTexData); + + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCubeShadow); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + depthTexData[0] = 0.2f; + glTexStorage2D(GL_TEXTURE_CUBE_MAP, 1, GL_DEPTH_COMPONENT32F, 1, 1); + glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, 0, 0, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, + depthTexData); + + EXPECT_GL_NO_ERROR(); + + glUseProgram(mProgram); + glUniform1f(mDepthRefUniformLocation, 0.3f); + glUniform1i(mTexture2DUniformLocation, 0); + glUniform1i(mTextureCubeUniformLocation, 1); + glUniform1i(mTexture2DShadowUniformLocation, 2); + glUniform1i(mTextureCubeShadowUniformLocation, 3); + + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + // The shader writes: + // + + // + + // 0.25 * + + // 0.125 * + EXPECT_PIXEL_NEAR(0, 0, 64, 154, 184, 255, 2); +} + class TextureLimitsTest : public ANGLETest { protected: @@ -1044,6 +1741,7 @@ TEST_P(TextureLimitsTest, MaxActiveFragmentTextures) } // Negative test for pointing two sampler uniforms of different types to the same texture. +// GLES 2.0.25 section 2.10.4 page 39. TEST_P(TextureLimitsTest, TextureTypeConflict) { const std::string &vertexShader = @@ -1083,7 +1781,9 @@ TEST_P(TextureLimitsTest, TextureTypeConflict) } // Negative test for rendering with texture outside the valid range. -// TODO(jmadill): Research if this is correct. +// TODO(jmadill): Possibly adjust the test according to the spec: +// GLES 3.0.4 section 2.12.7 mentions that specifying an out-of-range sampler uniform value +// generates an INVALID_VALUE error - GLES 2.0 doesn't yet have this mention. TEST_P(TextureLimitsTest, DrawWithTexturePastMaximum) { const std::string &vertexShader = @@ -1116,8 +1816,50 @@ TEST_P(TextureLimitsTest, DrawWithTexturePastMaximum) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(TextureTest, ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3()); // TODO(geofflang): Figure out why this test fails on Intel OpenGL -ANGLE_INSTANTIATE_TEST(TextureTestES3, ES3_D3D11(), ES3_OPENGL()); -ANGLE_INSTANTIATE_TEST(TextureLimitsTest, ES2_D3D11(), ES2_OPENGL()); +// TODO(oetuaho): Enable all below tests on OpenGL. Requires a fix for ANGLE bug 1278. +ANGLE_INSTANTIATE_TEST(Texture2DTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES2_OPENGLES()); +ANGLE_INSTANTIATE_TEST(TextureCubeTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES2_OPENGLES()); +ANGLE_INSTANTIATE_TEST(Texture2DTestWithDrawScale, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES2_OPENGLES()); +ANGLE_INSTANTIATE_TEST(Sampler2DAsFunctionParameterTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES2_OPENGLES()); +ANGLE_INSTANTIATE_TEST(SamplerArrayTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES2_OPENGLES()); +ANGLE_INSTANTIATE_TEST(SamplerArrayAsFunctionParameterTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES2_OPENGLES()); +ANGLE_INSTANTIATE_TEST(Texture2DTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); +ANGLE_INSTANTIATE_TEST(ShadowSamplerPlusSampler3DTestES3, + ES3_D3D11(), + ES3_OPENGL(), + ES3_OPENGLES()); +ANGLE_INSTANTIATE_TEST(SamplerTypeMixTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); +ANGLE_INSTANTIATE_TEST(Texture2DArrayTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); +ANGLE_INSTANTIATE_TEST(TextureLimitsTest, ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES()); } // namespace diff --git a/gfx/angle/src/tests/gl_tests/TimerQueriesTest.cpp b/gfx/angle/src/tests/gl_tests/TimerQueriesTest.cpp new file mode 100644 index 0000000000..70d9cfd0d2 --- /dev/null +++ b/gfx/angle/src/tests/gl_tests/TimerQueriesTest.cpp @@ -0,0 +1,582 @@ +// +// 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. +// +// TimerQueriesTest.cpp +// Various tests for EXT_disjoint_timer_query functionality and validation +// + +#include "system_utils.h" +#include "test_utils/ANGLETest.h" +#include "random_utils.h" + +using namespace angle; + +class TimerQueriesTest : public ANGLETest +{ + protected: + TimerQueriesTest() : mProgram(0), mProgramCostly(0) + { + setWindowWidth(128); + setWindowHeight(128); + setConfigRedBits(8); + setConfigGreenBits(8); + setConfigBlueBits(8); + setConfigAlphaBits(8); + setConfigDepthBits(24); + } + + virtual void SetUp() + { + ANGLETest::SetUp(); + + const std::string passthroughVS = + "attribute highp vec4 position; void main(void)\n" + "{\n" + " gl_Position = position;\n" + "}\n"; + + const std::string passthroughPS = + "precision highp float; void main(void)\n" + "{\n" + " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n" + "}\n"; + + const std::string costlyVS = + "attribute highp vec4 position; varying highp vec4 testPos; void main(void)\n" + "{\n" + " testPos = position;\n" + " gl_Position = position;\n" + "}\n"; + + const std::string costlyPS = + "precision highp float; varying highp vec4 testPos; void main(void)\n" + "{\n" + " vec4 test = testPos;\n" + " for (int i = 0; i < 500; i++)\n" + " {\n" + " test = sqrt(test);\n" + " }\n" + " gl_FragColor = test;\n" + "}\n"; + + mProgram = CompileProgram(passthroughVS, passthroughPS); + ASSERT_NE(0u, mProgram) << "shader compilation failed."; + + mProgramCostly = CompileProgram(costlyVS, costlyPS); + ASSERT_NE(0u, mProgramCostly) << "shader compilation failed."; + } + + virtual void TearDown() + { + glDeleteProgram(mProgram); + glDeleteProgram(mProgramCostly); + ANGLETest::TearDown(); + } + + GLuint mProgram; + GLuint mProgramCostly; +}; + +// Tests the time elapsed query +TEST_P(TimerQueriesTest, TimeElapsed) +{ + if (!extensionEnabled("GL_EXT_disjoint_timer_query")) + { + std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available." + << std::endl; + return; + } + + GLint queryTimeElapsedBits = 0; + glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits); + ASSERT_GL_NO_ERROR(); + + std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl; + + // Skip test if the number of bits is 0 + if (queryTimeElapsedBits == 0) + { + std::cout << "Test skipped because of 0 counter bits" << std::endl; + return; + } + + glDepthMask(GL_TRUE); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + GLuint query1 = 0; + GLuint query2 = 0; + glGenQueriesEXT(1, &query1); + glGenQueriesEXT(1, &query2); + + // Test time elapsed for a single quad + glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query1); + drawQuad(mProgram, "position", 0.8f); + glEndQueryEXT(GL_TIME_ELAPSED_EXT); + ASSERT_GL_NO_ERROR(); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + // Test time elapsed for costly quad + glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query2); + drawQuad(mProgramCostly, "position", 0.8f); + glEndQueryEXT(GL_TIME_ELAPSED_EXT); + ASSERT_GL_NO_ERROR(); + + swapBuffers(); + + int timeout = 200000; + GLuint ready = GL_FALSE; + while (ready == GL_FALSE && timeout > 0) + { + angle::Sleep(0); + glGetQueryObjectuivEXT(query1, GL_QUERY_RESULT_AVAILABLE_EXT, &ready); + timeout--; + } + ready = GL_FALSE; + while (ready == GL_FALSE && timeout > 0) + { + angle::Sleep(0); + glGetQueryObjectuivEXT(query2, GL_QUERY_RESULT_AVAILABLE_EXT, &ready); + timeout--; + } + ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl; + + GLuint64 result1 = 0; + GLuint64 result2 = 0; + glGetQueryObjectui64vEXT(query1, GL_QUERY_RESULT_EXT, &result1); + glGetQueryObjectui64vEXT(query2, GL_QUERY_RESULT_EXT, &result2); + ASSERT_GL_NO_ERROR(); + + glDeleteQueriesEXT(1, &query1); + glDeleteQueriesEXT(1, &query2); + ASSERT_GL_NO_ERROR(); + + std::cout << "Elapsed time: " << result1 << " cheap quad" << std::endl; + std::cout << "Elapsed time: " << result2 << " costly quad" << std::endl; + + // The time elapsed should be nonzero + EXPECT_LT(0ul, result1); + EXPECT_LT(0ul, result2); + + // The costly quad should take longer than the cheap quad + EXPECT_LT(result1, result2); +} + +// Tests time elapsed for a non draw call (texture upload) +TEST_P(TimerQueriesTest, TimeElapsedTextureTest) +{ + // OSX drivers don't seem to properly time non-draw calls so we skip the test on Mac + if (isOSX()) + { + std::cout << "Test skipped on OSX" << std::endl; + return; + } + + if (!extensionEnabled("GL_EXT_disjoint_timer_query")) + { + std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available." + << std::endl; + return; + } + + GLint queryTimeElapsedBits = 0; + glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits); + ASSERT_GL_NO_ERROR(); + + std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl; + + // Skip test if the number of bits is 0 + if (queryTimeElapsedBits == 0) + { + std::cout << "Test skipped because of 0 counter bits" << std::endl; + return; + } + + GLubyte pixels[] = {0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0}; + + // Query and texture initialization + GLuint texture; + GLuint query = 0; + glGenQueriesEXT(1, &query); + glGenTextures(1, &texture); + + // Upload a texture inside the query + glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels); + glGenerateMipmap(GL_TEXTURE_2D); + glFinish(); + glEndQueryEXT(GL_TIME_ELAPSED_EXT); + ASSERT_GL_NO_ERROR(); + + int timeout = 200000; + GLuint ready = GL_FALSE; + while (ready == GL_FALSE && timeout > 0) + { + angle::Sleep(0); + glGetQueryObjectuivEXT(query, GL_QUERY_RESULT_AVAILABLE_EXT, &ready); + timeout--; + } + ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl; + + GLuint64 result = 0; + glGetQueryObjectui64vEXT(query, GL_QUERY_RESULT_EXT, &result); + ASSERT_GL_NO_ERROR(); + + glDeleteTextures(1, &texture); + glDeleteQueriesEXT(1, &query); + + std::cout << "Elapsed time: " << result << std::endl; + EXPECT_LT(0ul, result); +} + +// Tests validation of query functions with respect to elapsed time query +TEST_P(TimerQueriesTest, TimeElapsedValidationTest) +{ + if (!extensionEnabled("GL_EXT_disjoint_timer_query")) + { + std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available." + << std::endl; + return; + } + + GLint queryTimeElapsedBits = 0; + glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits); + ASSERT_GL_NO_ERROR(); + + std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl; + + // Skip test if the number of bits is 0 + if (queryTimeElapsedBits == 0) + { + std::cout << "Test skipped because of 0 counter bits" << std::endl; + return; + } + + GLuint query = 0; + glGenQueriesEXT(-1, &query); + EXPECT_GL_ERROR(GL_INVALID_VALUE); + + glGenQueriesEXT(1, &query); + EXPECT_GL_NO_ERROR(); + + glBeginQueryEXT(GL_TIMESTAMP_EXT, query); + EXPECT_GL_ERROR(GL_INVALID_ENUM); + + glBeginQueryEXT(GL_TIME_ELAPSED_EXT, 0); + EXPECT_GL_ERROR(GL_INVALID_OPERATION); + + glEndQueryEXT(GL_TIME_ELAPSED_EXT); + EXPECT_GL_ERROR(GL_INVALID_OPERATION); + + glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query); + EXPECT_GL_NO_ERROR(); + + glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query); + EXPECT_GL_ERROR(GL_INVALID_OPERATION); + + glEndQueryEXT(GL_TIME_ELAPSED_EXT); + EXPECT_GL_NO_ERROR(); + + glEndQueryEXT(GL_TIME_ELAPSED_EXT); + EXPECT_GL_ERROR(GL_INVALID_OPERATION); +} + +// Tests timer queries operating under multiple EGL contexts with mid-query switching +TEST_P(TimerQueriesTest, TimeElapsedMulticontextTest) +{ + if (!extensionEnabled("GL_EXT_disjoint_timer_query")) + { + std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available." + << std::endl; + return; + } + + GLint queryTimeElapsedBits = 0; + glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits); + ASSERT_GL_NO_ERROR(); + + std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl; + + // Skip test if the number of bits is 0 + if (queryTimeElapsedBits == 0) + { + std::cout << "Test skipped because of 0 counter bits" << std::endl; + return; + } + + // D3D multicontext isn't implemented yet + if (GetParam() == ES3_D3D11() || GetParam() == ES2_D3D11()) + { + std::cout + << "Test skipped because the D3D backends cannot support simultaneous timer queries yet" + << std::endl; + return; + } + + EGLint contextAttributes[] = { + EGL_CONTEXT_MAJOR_VERSION_KHR, + GetParam().majorVersion, + EGL_CONTEXT_MINOR_VERSION_KHR, + GetParam().minorVersion, + EGL_NONE, + }; + + EGLWindow *window = getEGLWindow(); + + EGLDisplay display = window->getDisplay(); + EGLConfig config = window->getConfig(); + EGLSurface surface = window->getSurface(); + + struct ContextInfo + { + EGLContext context; + GLuint program; + GLuint query; + EGLDisplay display; + + ContextInfo() : context(EGL_NO_CONTEXT), program(0), query(0), display(EGL_NO_DISPLAY) {} + + ~ContextInfo() + { + if (context != EGL_NO_CONTEXT && display != EGL_NO_DISPLAY) + { + eglDestroyContext(display, context); + } + } + }; + ContextInfo contexts[2]; + + // Shaders + const std::string cheapVS = + "attribute highp vec4 position; void main(void)\n" + "{\n" + " gl_Position = position;\n" + "}\n"; + + const std::string cheapPS = + "precision highp float; void main(void)\n" + "{\n" + " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n" + "}\n"; + + const std::string costlyVS = + "attribute highp vec4 position; varying highp vec4 testPos; void main(void)\n" + "{\n" + " testPos = position;\n" + " gl_Position = position;\n" + "}\n"; + + const std::string costlyPS = + "precision highp float; varying highp vec4 testPos; void main(void)\n" + "{\n" + " vec4 test = testPos;\n" + " for (int i = 0; i < 500; i++)\n" + " {\n" + " test = sqrt(test);\n" + " }\n" + " gl_FragColor = test;\n" + "}\n"; + + // Setup the first context with a cheap shader + contexts[0].context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes); + contexts[0].display = display; + ASSERT_NE(contexts[0].context, EGL_NO_CONTEXT); + eglMakeCurrent(display, surface, surface, contexts[0].context); + contexts[0].program = CompileProgram(cheapVS, cheapPS); + glGenQueriesEXT(1, &contexts[0].query); + ASSERT_GL_NO_ERROR(); + + // Setup the second context with an expensive shader + contexts[1].context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes); + contexts[1].display = display; + ASSERT_NE(contexts[1].context, EGL_NO_CONTEXT); + eglMakeCurrent(display, surface, surface, contexts[1].context); + contexts[1].program = CompileProgram(costlyVS, costlyPS); + glGenQueriesEXT(1, &contexts[1].query); + ASSERT_GL_NO_ERROR(); + + // Start the query and draw a quad on the first context without ending the query + eglMakeCurrent(display, surface, surface, contexts[0].context); + glBeginQueryEXT(GL_TIME_ELAPSED_EXT, contexts[0].query); + drawQuad(contexts[0].program, "position", 0.8f); + ASSERT_GL_NO_ERROR(); + + // Switch contexts, draw the expensive quad and end its query + eglMakeCurrent(display, surface, surface, contexts[1].context); + glBeginQueryEXT(GL_TIME_ELAPSED_EXT, contexts[1].query); + drawQuad(contexts[1].program, "position", 0.8f); + glEndQueryEXT(GL_TIME_ELAPSED_EXT); + ASSERT_GL_NO_ERROR(); + + // Go back to the first context, end its query, and get the result + eglMakeCurrent(display, surface, surface, contexts[0].context); + glEndQueryEXT(GL_TIME_ELAPSED_EXT); + int timeout = 20000; + GLuint ready = GL_FALSE; + while (ready == GL_FALSE && timeout > 0) + { + angle::Sleep(0); + glGetQueryObjectuivEXT(contexts[0].query, GL_QUERY_RESULT_AVAILABLE_EXT, &ready); + timeout--; + } + ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl; + + GLuint64 result1 = 0; + GLuint64 result2 = 0; + glGetQueryObjectui64vEXT(contexts[0].query, GL_QUERY_RESULT_EXT, &result1); + glDeleteQueriesEXT(1, &contexts[0].query); + glDeleteProgram(contexts[0].program); + ASSERT_GL_NO_ERROR(); + + // Get the 2nd contexts results + eglMakeCurrent(display, surface, surface, contexts[1].context); + timeout = 20000; + ready = GL_FALSE; + while (ready == GL_FALSE && timeout > 0) + { + angle::Sleep(0); + glGetQueryObjectuivEXT(contexts[1].query, GL_QUERY_RESULT_AVAILABLE_EXT, &ready); + timeout--; + } + ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl; + glGetQueryObjectui64vEXT(contexts[1].query, GL_QUERY_RESULT_EXT, &result2); + glDeleteQueriesEXT(1, &contexts[1].query); + glDeleteProgram(contexts[1].program); + ASSERT_GL_NO_ERROR(); + + // Switch back to main context + eglMakeCurrent(display, surface, surface, window->getContext()); + + // Compare the results. The cheap quad should be smaller than the expensive one if + // virtualization is working correctly + std::cout << "Elapsed time: " << result1 << " cheap quad" << std::endl; + std::cout << "Elapsed time: " << result2 << " costly quad" << std::endl; + EXPECT_LT(0ul, result1); + EXPECT_LT(0ul, result2); + EXPECT_LT(result1, result2); +} + +// Tests GPU timestamp functionality +TEST_P(TimerQueriesTest, Timestamp) +{ + if (!extensionEnabled("GL_EXT_disjoint_timer_query")) + { + std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available." + << std::endl; + return; + } + + GLint queryTimestampBits = 0; + glGetQueryivEXT(GL_TIMESTAMP_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimestampBits); + ASSERT_GL_NO_ERROR(); + + std::cout << "Timestamp counter bits: " << queryTimestampBits << std::endl; + + // Macs for some reason return 0 bits so skip the test for now if either are 0 + if (queryTimestampBits == 0) + { + std::cout << "Test skipped because of 0 counter bits" << std::endl; + return; + } + + glDepthMask(GL_TRUE); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + GLuint query1 = 0; + GLuint query2 = 0; + glGenQueriesEXT(1, &query1); + glGenQueriesEXT(1, &query2); + glQueryCounterEXT(query1, GL_TIMESTAMP_EXT); + drawQuad(mProgram, "position", 0.8f); + glQueryCounterEXT(query2, GL_TIMESTAMP_EXT); + + ASSERT_GL_NO_ERROR(); + + swapBuffers(); + + int timeout = 200000; + GLuint ready = GL_FALSE; + while (ready == GL_FALSE && timeout > 0) + { + angle::Sleep(0); + glGetQueryObjectuivEXT(query1, GL_QUERY_RESULT_AVAILABLE_EXT, &ready); + timeout--; + } + ready = GL_FALSE; + while (ready == GL_FALSE && timeout > 0) + { + angle::Sleep(0); + glGetQueryObjectuivEXT(query2, GL_QUERY_RESULT_AVAILABLE_EXT, &ready); + timeout--; + } + ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl; + + GLuint64 result1 = 0; + GLuint64 result2 = 0; + glGetQueryObjectui64vEXT(query1, GL_QUERY_RESULT_EXT, &result1); + glGetQueryObjectui64vEXT(query2, GL_QUERY_RESULT_EXT, &result2); + + ASSERT_GL_NO_ERROR(); + + glDeleteQueriesEXT(1, &query1); + glDeleteQueriesEXT(1, &query2); + + std::cout << "Timestamps: " << result1 << " " << result2 << std::endl; + EXPECT_LT(0ul, result1); + EXPECT_LT(0ul, result2); + EXPECT_LT(result1, result2); +} + +class TimerQueriesTestES3 : public TimerQueriesTest +{ +}; + +// Tests getting timestamps via glGetInteger64v +TEST_P(TimerQueriesTestES3, TimestampGetInteger64) +{ + if (!extensionEnabled("GL_EXT_disjoint_timer_query")) + { + std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available." + << std::endl; + return; + } + + GLint queryTimestampBits = 0; + glGetQueryivEXT(GL_TIMESTAMP_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimestampBits); + ASSERT_GL_NO_ERROR(); + + std::cout << "Timestamp counter bits: " << queryTimestampBits << std::endl; + + if (queryTimestampBits == 0) + { + std::cout << "Test skipped because of 0 counter bits" << std::endl; + return; + } + + glDepthMask(GL_TRUE); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + GLint64 result1 = 0; + GLint64 result2 = 0; + glGetInteger64v(GL_TIMESTAMP_EXT, &result1); + drawQuad(mProgram, "position", 0.8f); + glGetInteger64v(GL_TIMESTAMP_EXT, &result2); + ASSERT_GL_NO_ERROR(); + std::cout << "Timestamps (getInteger64v): " << result1 << " " << result2 << std::endl; + EXPECT_LT(0l, result1); + EXPECT_LT(0l, result2); + EXPECT_LT(result1, result2); +} + +ANGLE_INSTANTIATE_TEST(TimerQueriesTest, + ES2_D3D9(), + ES2_D3D11(), + ES3_D3D11(), + ES2_OPENGL(), + ES3_OPENGL()); + +ANGLE_INSTANTIATE_TEST(TimerQueriesTestES3, ES3_D3D11(), ES3_OPENGL()); \ No newline at end of file diff --git a/gfx/angle/src/tests/gl_tests/TransformFeedbackTest.cpp b/gfx/angle/src/tests/gl_tests/TransformFeedbackTest.cpp index 878897d5ab..fc4ed36c9d 100644 --- a/gfx/angle/src/tests/gl_tests/TransformFeedbackTest.cpp +++ b/gfx/angle/src/tests/gl_tests/TransformFeedbackTest.cpp @@ -700,6 +700,6 @@ TEST_P(TransformFeedbackTest, PackingBug) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(TransformFeedbackTest, ES3_D3D11(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(TransformFeedbackTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); } // anonymous namespace diff --git a/gfx/angle/src/tests/gl_tests/UniformBufferTest.cpp b/gfx/angle/src/tests/gl_tests/UniformBufferTest.cpp index f8078e57dc..5702771bb7 100644 --- a/gfx/angle/src/tests/gl_tests/UniformBufferTest.cpp +++ b/gfx/angle/src/tests/gl_tests/UniformBufferTest.cpp @@ -246,7 +246,8 @@ TEST_P(UniformBufferTest, UnboundUniformBuffer) TEST_P(UniformBufferTest, UniformBufferManyUpdates) { // TODO(jmadill): Figure out why this fails on Intel. - if (isIntel() && GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) + if (isIntel() && (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE || + getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)) { std::cout << "Test skipped on Intel." << std::endl; return; @@ -412,6 +413,11 @@ TEST_P(UniformBufferTest, ActiveUniformNames) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(UniformBufferTest, ES3_D3D11(), ES3_D3D11_FL11_1(), ES3_D3D11_FL11_1_REFERENCE()); +ANGLE_INSTANTIATE_TEST(UniformBufferTest, + ES3_D3D11(), + ES3_D3D11_FL11_1(), + ES3_D3D11_FL11_1_REFERENCE(), + ES3_OPENGL(), + ES3_OPENGLES()); } // namespace diff --git a/gfx/angle/src/tests/gl_tests/UniformTest.cpp b/gfx/angle/src/tests/gl_tests/UniformTest.cpp index 67467f0987..194b50bc17 100644 --- a/gfx/angle/src/tests/gl_tests/UniformTest.cpp +++ b/gfx/angle/src/tests/gl_tests/UniformTest.cpp @@ -501,7 +501,12 @@ TEST_P(UniformTest, SamplerUniformsAppearOnce) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(UniformTest, ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3(), ES2_OPENGL()); -ANGLE_INSTANTIATE_TEST(UniformTestES3, ES3_D3D11(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(UniformTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES2_OPENGLES()); +ANGLE_INSTANTIATE_TEST(UniformTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); } // namespace diff --git a/gfx/angle/src/tests/gl_tests/UnpackAlignmentTest.cpp b/gfx/angle/src/tests/gl_tests/UnpackAlignmentTest.cpp index 5c4905254b..2a3f76bc6f 100644 --- a/gfx/angle/src/tests/gl_tests/UnpackAlignmentTest.cpp +++ b/gfx/angle/src/tests/gl_tests/UnpackAlignmentTest.cpp @@ -314,6 +314,12 @@ TEST_P(UnpackAlignmentTest, Alignment8AUByte) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(UnpackAlignmentTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(UnpackAlignmentTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); } // namespace diff --git a/gfx/angle/src/tests/gl_tests/UnpackRowLength.cpp b/gfx/angle/src/tests/gl_tests/UnpackRowLength.cpp index 63ea8a9a6b..12db14d8fa 100644 --- a/gfx/angle/src/tests/gl_tests/UnpackRowLength.cpp +++ b/gfx/angle/src/tests/gl_tests/UnpackRowLength.cpp @@ -123,6 +123,8 @@ ANGLE_INSTANTIATE_TEST(UnpackRowLengthTest, ES2_D3D11(), ES2_D3D9(), ES2_OPENGL(), - ES3_OPENGL()); + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); } // namespace diff --git a/gfx/angle/src/tests/gl_tests/VertexAttributeTest.cpp b/gfx/angle/src/tests/gl_tests/VertexAttributeTest.cpp index 2449e335cb..3b574b8786 100644 --- a/gfx/angle/src/tests/gl_tests/VertexAttributeTest.cpp +++ b/gfx/angle/src/tests/gl_tests/VertexAttributeTest.cpp @@ -358,6 +358,13 @@ TEST_P(VertexAttributeTest, SimpleBindAttribLocation) // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. // D3D11 Feature Level 9_3 uses different D3D formats for vertex attribs compared to Feature Levels 10_0+, so we should test them separately. -ANGLE_INSTANTIATE_TEST(VertexAttributeTest, ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3(), ES2_OPENGL(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(VertexAttributeTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); } // namespace diff --git a/gfx/angle/src/tests/gl_tests/ViewportTest.cpp b/gfx/angle/src/tests/gl_tests/ViewportTest.cpp index f8c98165c7..8cd9cd3df2 100644 --- a/gfx/angle/src/tests/gl_tests/ViewportTest.cpp +++ b/gfx/angle/src/tests/gl_tests/ViewportTest.cpp @@ -261,6 +261,12 @@ TEST_P(ViewportTest, TripleWindowOffCenter) // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. // D3D11 Feature Level 9 and D3D9 emulate large and negative viewports in the vertex shader. We should test both of these as well as D3D11 Feature Level 10_0+. -ANGLE_INSTANTIATE_TEST(ViewportTest, ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3()); +ANGLE_INSTANTIATE_TEST(ViewportTest, + ES2_D3D9(), + ES2_D3D11(EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE), + ES2_D3D11(EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE), + ES2_D3D11_FL9_3(), + ES2_OPENGLES(), + ES3_OPENGLES()); } // namespace diff --git a/gfx/angle/src/tests/perf_tests/ANGLEPerfTest.cpp b/gfx/angle/src/tests/perf_tests/ANGLEPerfTest.cpp index 6681fa74b3..e7c74370de 100644 --- a/gfx/angle/src/tests/perf_tests/ANGLEPerfTest.cpp +++ b/gfx/angle/src/tests/perf_tests/ANGLEPerfTest.cpp @@ -3,20 +3,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // +// ANGLEPerfTests: +// Base class for google test performance tests +// #include "ANGLEPerfTest.h" #include "third_party/perf/perf_test.h" -#include #include +#include +#include ANGLEPerfTest::ANGLEPerfTest(const std::string &name, const std::string &suffix) : mName(name), mSuffix(suffix), - mRunning(false), mTimer(nullptr), - mNumFrames(0) + mRunTimeSeconds(5.0), + mNumStepsPerformed(0), + mRunning(true) { mTimer = CreateTimer(); } @@ -29,23 +34,20 @@ ANGLEPerfTest::~ANGLEPerfTest() void ANGLEPerfTest::run() { mTimer->start(); - double prevTime = 0.0; - while (mRunning) { - double elapsedTime = mTimer->getElapsedTime(); - double deltaTime = elapsedTime - prevTime; - - ++mNumFrames; - step(static_cast(deltaTime), elapsedTime); - - if (!mRunning) + step(); + if (mRunning) { - break; + ++mNumStepsPerformed; + } + if (mTimer->getElapsedTime() > mRunTimeSeconds) + { + mRunning = false; } - - prevTime = elapsedTime; } + finishTest(); + mTimer->stop(); } void ANGLEPerfTest::printResult(const std::string &trace, double value, const std::string &units, bool important) const @@ -60,37 +62,42 @@ void ANGLEPerfTest::printResult(const std::string &trace, size_t value, const st void ANGLEPerfTest::SetUp() { - mRunning = true; } void ANGLEPerfTest::TearDown() { - printResult("score", static_cast(mNumFrames), "score", true); + double relativeScore = static_cast(mNumStepsPerformed) / mTimer->getElapsedTime(); + printResult("score", static_cast(std::round(relativeScore)), "score", true); } double ANGLEPerfTest::normalizedTime(size_t value) const { - return static_cast(value) / static_cast(mNumFrames); + return static_cast(value) / static_cast(mNumStepsPerformed); } std::string RenderTestParams::suffix() const { switch (getRenderer()) { - case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE: return "_d3d11"; - case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE: return "_d3d9"; - case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE: return "_gl"; - case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE: return "_gles"; - case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE: return "_default"; - default: assert(0); return "_unk"; + case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE: + return "_d3d11"; + case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE: + return "_d3d9"; + case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE: + return "_gl"; + case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE: + return "_gles"; + case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE: + return "_default"; + default: + assert(0); + return "_unk"; } } ANGLERenderTest::ANGLERenderTest(const std::string &name, const RenderTestParams &testParams) : ANGLEPerfTest(name, testParams.suffix()), mTestParams(testParams), - mDrawIterations(10), - mRunTimeSeconds(5.0), mEGLWindow(nullptr), mOSWindow(nullptr) { @@ -136,47 +143,44 @@ void ANGLERenderTest::TearDown() mOSWindow->destroy(); } -void ANGLERenderTest::step(float dt, double totalTime) +void ANGLERenderTest::step() { - stepBenchmark(dt, totalTime); - // Clear events that the application did not process from this frame Event event; + bool closed = false; while (popEvent(&event)) { // If the application did not catch a close event, close now if (event.Type == Event::EVENT_CLOSED) { - mRunning = false; + closed = true; } } - if (mRunning) + if (closed) { - draw(); - mEGLWindow->swap(); + abortTest(); + } + else + { + drawBenchmark(); + // Swap is needed so that the GPU driver will occasionally flush its internal command queue + // to the GPU. The null device benchmarks are only testing CPU overhead, so they don't need + // to swap. + if (mTestParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE) + { + mEGLWindow->swap(); + } mOSWindow->messageLoop(); } } -void ANGLERenderTest::draw() +void ANGLERenderTest::finishTest() { - if (mTimer->getElapsedTime() > mRunTimeSeconds) + if (mTestParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE) { - mRunning = false; - return; + glFinish(); } - - ++mNumFrames; - - beginDrawBenchmark(); - - for (unsigned int iteration = 0; iteration < mDrawIterations; ++iteration) - { - drawBenchmark(); - } - - endDrawBenchmark(); } bool ANGLERenderTest::popEvent(Event *event) diff --git a/gfx/angle/src/tests/perf_tests/ANGLEPerfTest.h b/gfx/angle/src/tests/perf_tests/ANGLEPerfTest.h index dc1eff50a2..1afef8cdf4 100644 --- a/gfx/angle/src/tests/perf_tests/ANGLEPerfTest.h +++ b/gfx/angle/src/tests/perf_tests/ANGLEPerfTest.h @@ -10,24 +10,25 @@ #ifndef PERF_TESTS_ANGLE_PERF_TEST_H_ #define PERF_TESTS_ANGLE_PERF_TEST_H_ -#include #include #include + +#include #include #include -#include "EGLWindow.h" -#include "OSWindow.h" -#include "Timer.h" #include "common/angleutils.h" #include "common/debug.h" +#include "EGLWindow.h" +#include "OSWindow.h" #include "test_utils/angle_test_configs.h" #include "test_utils/angle_test_instantiate.h" +#include "Timer.h" class Event; #ifndef ASSERT_GL_NO_ERROR -#define ASSERT_GL_NO_ERROR() ASSERT_TRUE(glGetError() == GL_NO_ERROR) +#define ASSERT_GL_NO_ERROR() ASSERT_EQ(static_cast(GL_NO_ERROR), glGetError()) #endif class ANGLEPerfTest : public testing::Test, angle::NonCopyable @@ -36,7 +37,10 @@ class ANGLEPerfTest : public testing::Test, angle::NonCopyable ANGLEPerfTest(const std::string &name, const std::string &suffix); virtual ~ANGLEPerfTest(); - virtual void step(float dt, double totalTime) = 0; + virtual void step() = 0; + + // Called right before timer is stopped to let the test wait for asynchronous operations. + virtual void finishTest() {} protected: void run(); @@ -48,12 +52,19 @@ class ANGLEPerfTest : public testing::Test, angle::NonCopyable // Normalize a time value according to the number of test loop iterations (mFrameCount) double normalizedTime(size_t value) const; + // Call if the test step was aborted and the test should stop running. + void abortTest() { mRunning = false; } + + int getNumStepsPerformed() const { return mNumStepsPerformed; } + std::string mName; std::string mSuffix; - - bool mRunning; Timer *mTimer; - int mNumFrames; + double mRunTimeSeconds; + + private: + int mNumStepsPerformed; + bool mRunning; }; struct RenderTestParams : public angle::PlatformParameters @@ -73,11 +84,7 @@ class ANGLERenderTest : public ANGLEPerfTest virtual void initializeBenchmark() { } virtual void destroyBenchmark() { } - virtual void stepBenchmark(float dt, double totalTime) { } - - virtual void beginDrawBenchmark() { } virtual void drawBenchmark() = 0; - virtual void endDrawBenchmark() { } bool popEvent(Event *event); @@ -85,15 +92,13 @@ class ANGLERenderTest : public ANGLEPerfTest protected: const RenderTestParams &mTestParams; - unsigned int mDrawIterations; - double mRunTimeSeconds; private: void SetUp() override; void TearDown() override; - void step(float dt, double totalTime) override; - void draw(); + void step() override; + void finishTest() override; EGLWindow *mEGLWindow; OSWindow *mOSWindow; diff --git a/gfx/angle/src/tests/perf_tests/BufferSubData.cpp b/gfx/angle/src/tests/perf_tests/BufferSubData.cpp index d153b0c936..5818d52ee9 100644 --- a/gfx/angle/src/tests/perf_tests/BufferSubData.cpp +++ b/gfx/angle/src/tests/perf_tests/BufferSubData.cpp @@ -28,7 +28,7 @@ struct BufferSubDataParams final : public RenderTestParams windowHeight = 512; updateSize = 3000; bufferSize = 40000000; - iterations = 2; + iterations = 4; updateRate = 1; } @@ -59,7 +59,6 @@ class BufferSubDataBenchmark : public ANGLERenderTest, void initializeBenchmark() override; void destroyBenchmark() override; - void beginDrawBenchmark() override; void drawBenchmark() override; private: @@ -94,10 +93,14 @@ GLfloat *GetFloatData(GLint componentCount) switch (componentCount) { - case 2: return vertices2; - case 3: return vertices3; - case 4: return vertices4; - default: return NULL; + case 2: + return vertices2; + case 3: + return vertices3; + case 4: + return vertices4; + default: + return nullptr; } } @@ -151,13 +154,26 @@ GLsizeiptr GetVertexData(GLenum type, GLint componentCount, GLboolean normalized switch (type) { - case GL_BYTE: triDataSize = GetNormalizedData(numElements, floatData, data); break; - case GL_SHORT: triDataSize = GetNormalizedData(numElements, floatData, data); break; - case GL_INT: triDataSize = GetNormalizedData(numElements, floatData, data); break; - case GL_UNSIGNED_BYTE: triDataSize = GetNormalizedData(numElements, floatData, data); break; - case GL_UNSIGNED_SHORT: triDataSize = GetNormalizedData(numElements, floatData, data); break; - case GL_UNSIGNED_INT: triDataSize = GetNormalizedData(numElements, floatData, data); break; - default: assert(0); + case GL_BYTE: + triDataSize = GetNormalizedData(numElements, floatData, data); + break; + case GL_SHORT: + triDataSize = GetNormalizedData(numElements, floatData, data); + break; + case GL_INT: + triDataSize = GetNormalizedData(numElements, floatData, data); + break; + case GL_UNSIGNED_BYTE: + triDataSize = GetNormalizedData(numElements, floatData, data); + break; + case GL_UNSIGNED_SHORT: + triDataSize = GetNormalizedData(numElements, floatData, data); + break; + case GL_UNSIGNED_INT: + triDataSize = GetNormalizedData(numElements, floatData, data); + break; + default: + assert(0); } } else @@ -166,13 +182,26 @@ GLsizeiptr GetVertexData(GLenum type, GLint componentCount, GLboolean normalized switch (type) { - case GL_BYTE: triDataSize = GetIntData(numElements, floatData, data); break; - case GL_SHORT: triDataSize = GetIntData(numElements, floatData, data); break; - case GL_INT: triDataSize = GetIntData(numElements, floatData, data); break; - case GL_UNSIGNED_BYTE: triDataSize = GetIntData(numElements, floatData, data); break; - case GL_UNSIGNED_SHORT: triDataSize = GetIntData(numElements, floatData, data); break; - case GL_UNSIGNED_INT: triDataSize = GetIntData(numElements, floatData, data); break; - default: assert(0); + case GL_BYTE: + triDataSize = GetIntData(numElements, floatData, data); + break; + case GL_SHORT: + triDataSize = GetIntData(numElements, floatData, data); + break; + case GL_INT: + triDataSize = GetIntData(numElements, floatData, data); + break; + case GL_UNSIGNED_BYTE: + triDataSize = GetIntData(numElements, floatData, data); + break; + case GL_UNSIGNED_SHORT: + triDataSize = GetIntData(numElements, floatData, data); + break; + case GL_UNSIGNED_INT: + triDataSize = GetIntData(numElements, floatData, data); + break; + default: + assert(0); } } @@ -192,14 +221,30 @@ std::string BufferSubDataParams::suffix() const switch (vertexType) { - case GL_FLOAT: strstr << "_float"; break; - case GL_INT: strstr << "_int"; break; - case GL_BYTE: strstr << "_byte"; break; - case GL_SHORT: strstr << "_short"; break; - case GL_UNSIGNED_INT: strstr << "_uint"; break; - case GL_UNSIGNED_BYTE: strstr << "_ubyte"; break; - case GL_UNSIGNED_SHORT: strstr << "_ushort"; break; - default: strstr << "_vunk_" << vertexType << "_"; break; + case GL_FLOAT: + strstr << "_float"; + break; + case GL_INT: + strstr << "_int"; + break; + case GL_BYTE: + strstr << "_byte"; + break; + case GL_SHORT: + strstr << "_short"; + break; + case GL_UNSIGNED_INT: + strstr << "_uint"; + break; + case GL_UNSIGNED_BYTE: + strstr << "_ubyte"; + break; + case GL_UNSIGNED_SHORT: + strstr << "_ushort"; + break; + default: + strstr << "_vunk_" << vertexType << "_"; + break; } strstr << vertexComponentCount; @@ -212,7 +257,7 @@ BufferSubDataBenchmark::BufferSubDataBenchmark() : ANGLERenderTest("BufferSubData", GetParam()), mProgram(0), mBuffer(0), - mUpdateData(NULL), + mUpdateData(nullptr), mNumTris(0) { } @@ -221,9 +266,8 @@ void BufferSubDataBenchmark::initializeBenchmark() { const auto ¶ms = GetParam(); - ASSERT_TRUE(params.vertexComponentCount > 1); - ASSERT_TRUE(params.iterations > 0); - mDrawIterations = params.iterations; + ASSERT_LT(1, params.vertexComponentCount); + ASSERT_LT(0u, params.iterations); const std::string vs = SHADER_SOURCE ( @@ -246,7 +290,7 @@ void BufferSubDataBenchmark::initializeBenchmark() ); mProgram = CompileProgram(vs, fs); - ASSERT_TRUE(mProgram != 0); + ASSERT_NE(0u, mProgram); // Use the program object glUseProgram(mProgram); @@ -313,19 +357,15 @@ void BufferSubDataBenchmark::destroyBenchmark() SafeDeleteArray(mUpdateData); } -void BufferSubDataBenchmark::beginDrawBenchmark() -{ - // Clear the color buffer - glClear(GL_COLOR_BUFFER_BIT); -} - void BufferSubDataBenchmark::drawBenchmark() { + glClear(GL_COLOR_BUFFER_BIT); + const auto ¶ms = GetParam(); for (unsigned int it = 0; it < params.iterations; it++) { - if (params.updateSize > 0 && ((mNumFrames % params.updateRate) == 0)) + if (params.updateSize > 0 && ((getNumStepsPerformed() % params.updateRate) == 0)) { glBufferSubData(GL_ARRAY_BUFFER, 0, params.updateSize, mUpdateData); } diff --git a/gfx/angle/src/tests/perf_tests/DrawCallPerf.cpp b/gfx/angle/src/tests/perf_tests/DrawCallPerf.cpp index d0fb539e8c..b8c82d54e7 100644 --- a/gfx/angle/src/tests/perf_tests/DrawCallPerf.cpp +++ b/gfx/angle/src/tests/perf_tests/DrawCallPerf.cpp @@ -69,7 +69,6 @@ class DrawCallPerfBenchmark : public ANGLERenderTest, void initializeBenchmark() override; void destroyBenchmark() override; - void beginDrawBenchmark() override; void drawBenchmark() override; private: @@ -91,8 +90,7 @@ void DrawCallPerfBenchmark::initializeBenchmark() { const auto ¶ms = GetParam(); - ASSERT_TRUE(params.iterations > 0); - mDrawIterations = params.iterations; + ASSERT_LT(0u, params.iterations); const std::string vs = SHADER_SOURCE ( @@ -115,7 +113,7 @@ void DrawCallPerfBenchmark::initializeBenchmark() ); mProgram = CompileProgram(vs, fs); - ASSERT_TRUE(mProgram != 0); + ASSERT_NE(0u, mProgram); // Use the program object glUseProgram(mProgram); @@ -166,14 +164,10 @@ void DrawCallPerfBenchmark::destroyBenchmark() glDeleteBuffers(1, &mBuffer); } -void DrawCallPerfBenchmark::beginDrawBenchmark() -{ - // Clear the color buffer - glClear(GL_COLOR_BUFFER_BIT); -} - void DrawCallPerfBenchmark::drawBenchmark() { + glClear(GL_COLOR_BUFFER_BIT); + const auto ¶ms = GetParam(); for (unsigned int it = 0; it < params.iterations; it++) @@ -211,7 +205,7 @@ DrawCallPerfParams DrawCallPerfValidationOnly() { DrawCallPerfParams params; params.eglParameters = DEFAULT(); - params.iterations = 100; + params.iterations = 10000; params.numTris = 0; params.runTimeSeconds = 5.0; return params; diff --git a/gfx/angle/src/tests/perf_tests/EGLInitializePerf.cpp b/gfx/angle/src/tests/perf_tests/EGLInitializePerf.cpp index e6f9d5577c..210ba2745c 100644 --- a/gfx/angle/src/tests/perf_tests/EGLInitializePerf.cpp +++ b/gfx/angle/src/tests/perf_tests/EGLInitializePerf.cpp @@ -77,7 +77,7 @@ class EGLInitializePerfTest : public ANGLEPerfTest, EGLInitializePerfTest(); ~EGLInitializePerfTest(); - void step(float dt, double totalTime) override; + void step() override; void TearDown() override; private: @@ -121,7 +121,7 @@ EGLInitializePerfTest::EGLInitializePerfTest() } mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, - mOSWindow->getNativeDisplay(), + reinterpret_cast(mOSWindow->getNativeDisplay()), &displayAttributes[0]); ANGLEPlatformInitialize(&mCapturePlatform); @@ -132,18 +132,13 @@ EGLInitializePerfTest::~EGLInitializePerfTest() SafeDelete(mOSWindow); } -void EGLInitializePerfTest::step(float dt, double totalTime) +void EGLInitializePerfTest::step() { - ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY); + ASSERT_NE(EGL_NO_DISPLAY, mDisplay); EGLint majorVersion, minorVersion; - ASSERT_TRUE(eglInitialize(mDisplay, &majorVersion, &minorVersion) == EGL_TRUE); - ASSERT_TRUE(eglTerminate(mDisplay) == EGL_TRUE); - - if (mTimer->getElapsedTime() >= 5.0) - { - mRunning = false; - } + ASSERT_EQ(static_cast(EGL_TRUE), eglInitialize(mDisplay, &majorVersion, &minorVersion)); + ASSERT_EQ(static_cast(EGL_TRUE), eglTerminate(mDisplay)); } void EGLInitializePerfTest::TearDown() diff --git a/gfx/angle/src/tests/perf_tests/IndexConversionPerf.cpp b/gfx/angle/src/tests/perf_tests/IndexConversionPerf.cpp index ff7264ed34..39a81ca48c 100644 --- a/gfx/angle/src/tests/perf_tests/IndexConversionPerf.cpp +++ b/gfx/angle/src/tests/perf_tests/IndexConversionPerf.cpp @@ -50,7 +50,6 @@ class IndexConversionPerfTest : public ANGLERenderTest, void initializeBenchmark() override; void destroyBenchmark() override; - void beginDrawBenchmark() override; void drawBenchmark() override; void updateBufferData(); @@ -75,10 +74,8 @@ void IndexConversionPerfTest::initializeBenchmark() { const auto ¶ms = GetParam(); - ASSERT_TRUE(params.iterations > 0); - ASSERT_TRUE(params.numIndexTris > 0); - - mDrawIterations = params.iterations; + ASSERT_LT(0u, params.iterations); + ASSERT_LT(0u, params.numIndexTris); const std::string vs = SHADER_SOURCE ( @@ -101,7 +98,7 @@ void IndexConversionPerfTest::initializeBenchmark() ); mProgram = CompileProgram(vs, fs); - ASSERT_TRUE(mProgram != 0); + ASSERT_NE(0u, mProgram); // Use the program object glUseProgram(mProgram); @@ -150,7 +147,7 @@ void IndexConversionPerfTest::initializeBenchmark() glUniform1f(glGetUniformLocation(mProgram, "uScale"), scale); glUniform1f(glGetUniformLocation(mProgram, "uOffset"), offset); - ASSERT_TRUE(glGetError() == GL_NO_ERROR); + ASSERT_GL_NO_ERROR(); } void IndexConversionPerfTest::updateBufferData() @@ -165,14 +162,10 @@ void IndexConversionPerfTest::destroyBenchmark() glDeleteBuffers(1, &mIndexBuffer); } -void IndexConversionPerfTest::beginDrawBenchmark() -{ - // Clear the color buffer - glClear(GL_COLOR_BUFFER_BIT); -} - void IndexConversionPerfTest::drawBenchmark() { + glClear(GL_COLOR_BUFFER_BIT); + const auto ¶ms = GetParam(); // Trigger an update to ensure we convert once a frame @@ -186,7 +179,7 @@ void IndexConversionPerfTest::drawBenchmark() reinterpret_cast(0)); } - EXPECT_TRUE(glGetError() == GL_NO_ERROR); + ASSERT_GL_NO_ERROR(); } IndexConversionPerfParams IndexConversionPerfD3D11Params() @@ -197,7 +190,7 @@ IndexConversionPerfParams IndexConversionPerfD3D11Params() params.minorVersion = 0; params.windowWidth = 256; params.windowHeight = 256; - params.iterations = 15; + params.iterations = 225; params.numIndexTris = 3000; return params; } diff --git a/gfx/angle/src/tests/perf_tests/IndexDataManagerTest.cpp b/gfx/angle/src/tests/perf_tests/IndexDataManagerTest.cpp index 40ca085684..ded8ed1e8d 100644 --- a/gfx/angle/src/tests/perf_tests/IndexDataManagerTest.cpp +++ b/gfx/angle/src/tests/perf_tests/IndexDataManagerTest.cpp @@ -116,7 +116,7 @@ class IndexDataManagerPerfTest : public ANGLEPerfTest public: IndexDataManagerPerfTest(); - void step(float dt, double totalTime) override; + void step() override; rx::IndexDataManager mIndexDataManager; GLsizei mIndexCount; @@ -151,21 +151,15 @@ IndexDataManagerPerfTest::IndexDataManagerPerfTest() mIndexBuffer.bufferData(&indexData[0], indexData.size() * sizeof(GLushort), GL_STATIC_DRAW); } -void IndexDataManagerPerfTest::step(float dt, double totalTime) +void IndexDataManagerPerfTest::step() { rx::TranslatedIndexData translatedIndexData; - rx::SourceIndexData sourceIndexData; for (unsigned int iteration = 0; iteration < 100; ++iteration) { mIndexBuffer.getIndexRange(GL_UNSIGNED_SHORT, 0, mIndexCount, false, &translatedIndexData.indexRange); mIndexDataManager.prepareIndexData(GL_UNSIGNED_SHORT, mIndexCount, &mIndexBuffer, nullptr, - &translatedIndexData, &sourceIndexData, false); - } - - if (mTimer->getElapsedTime() >= 5.0) - { - mRunning = false; + &translatedIndexData, false); } } diff --git a/gfx/angle/src/tests/perf_tests/InstancingPerf.cpp b/gfx/angle/src/tests/perf_tests/InstancingPerf.cpp new file mode 100644 index 0000000000..eb02c10e99 --- /dev/null +++ b/gfx/angle/src/tests/perf_tests/InstancingPerf.cpp @@ -0,0 +1,366 @@ +// +// Copyright 2015 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. +// +// InstancingPerf: +// Performance tests for ANGLE instanced draw calls. +// + +#include +#include + +#include "ANGLEPerfTest.h" +#include "Matrix.h" +#include "random_utils.h" +#include "shader_utils.h" +#include "Vector.h" + +using namespace angle; +using namespace egl_platform; + +namespace +{ + +float AnimationSignal(float t) +{ + float l = t / 2.0f; + float f = l - std::floor(l); + return (f > 0.5f ? 1.0f - f : f) * 4.0f - 1.0f; +} + +template +size_t VectorSizeBytes(const std::vector &vec) +{ + return sizeof(T) * vec.size(); +} + +Vector3 RandomVector3(RNG *rng) +{ + return Vector3(rng->randomNegativeOneToOne(), rng->randomNegativeOneToOne(), + rng->randomNegativeOneToOne()); +} + +struct InstancingPerfParams final : public RenderTestParams +{ + // Common default options + InstancingPerfParams() + { + majorVersion = 2; + minorVersion = 0; + windowWidth = 256; + windowHeight = 256; + iterations = 1; + runTimeSeconds = 10.0; + animationEnabled = false; + instancingEnabled = true; + } + + std::string suffix() const override + { + std::stringstream strstr; + + strstr << RenderTestParams::suffix(); + + if (!instancingEnabled) + { + strstr << "_billboards"; + } + + return strstr.str(); + } + + unsigned int iterations; + double runTimeSeconds; + bool animationEnabled; + bool instancingEnabled; +}; + +std::ostream &operator<<(std::ostream &os, const InstancingPerfParams ¶ms) +{ + os << params.suffix().substr(1); + return os; +} + +class InstancingPerfBenchmark : public ANGLERenderTest, + public ::testing::WithParamInterface +{ + public: + InstancingPerfBenchmark(); + + void initializeBenchmark() override; + void destroyBenchmark() override; + void drawBenchmark() override; + + private: + GLuint mProgram; + std::vector mBuffers; + GLuint mNumPoints; + std::vector mTranslateData; + std::vector mSizeData; + std::vector mColorData; + angle::RNG mRNG; +}; + +InstancingPerfBenchmark::InstancingPerfBenchmark() + : ANGLERenderTest("InstancingPerf", GetParam()), mProgram(0), mNumPoints(75000) +{ + mRunTimeSeconds = GetParam().runTimeSeconds; +} + +void InstancingPerfBenchmark::initializeBenchmark() +{ + const auto ¶ms = GetParam(); + + ASSERT_LT(0u, params.iterations); + + const std::string vs = + "attribute vec2 aPosition;\n" + "attribute vec3 aTranslate;\n" + "attribute float aScale;\n" + "attribute vec3 aColor;\n" + "uniform mat4 uWorldMatrix;\n" + "uniform mat4 uProjectionMatrix;\n" + "varying vec3 vColor;\n" + "void main()\n" + "{\n" + " vec4 position = uWorldMatrix * vec4(aTranslate, 1.0);\n" + " position.xy += aPosition * aScale;\n" + " gl_Position = uProjectionMatrix * position;\n" + " vColor = aColor;\n" + "}\n"; + + const std::string fs = + "precision mediump float;\n" + "varying vec3 vColor;\n" + "void main()\n" + "{\n" + " gl_FragColor = vec4(vColor, 1.0);\n" + "}\n"; + + mProgram = CompileProgram(vs, fs); + ASSERT_NE(0u, mProgram); + + glUseProgram(mProgram); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + GLuint baseIndexData[6] = {0, 1, 2, 1, 3, 2}; + Vector2 basePositionData[4] = {Vector2(-1.0f, 1.0f), Vector2(1.0f, 1.0f), Vector2(-1.0f, -1.0f), + Vector2(1.0f, -1.0f)}; + + std::vector indexData; + std::vector positionData; + + if (!params.instancingEnabled) + { + GLuint pointVertexStride = 4; + for (GLuint pointIndex = 0; pointIndex < mNumPoints; ++pointIndex) + { + for (GLuint indexIndex = 0; indexIndex < 6; ++indexIndex) + { + indexData.push_back(baseIndexData[indexIndex] + pointIndex * pointVertexStride); + } + + Vector3 randVec = RandomVector3(&mRNG); + for (GLuint vertexIndex = 0; vertexIndex < 4; ++vertexIndex) + { + positionData.push_back(basePositionData[vertexIndex]); + mTranslateData.push_back(randVec); + } + } + + mSizeData.resize(mNumPoints * 4, 0.012f); + mColorData.resize(mNumPoints * 4, Vector3(1.0f, 0.0f, 0.0f)); + } + else + { + for (GLuint index : baseIndexData) + { + indexData.push_back(index); + } + + for (const Vector2 &position : basePositionData) + { + positionData.push_back(position); + } + + for (GLuint pointIndex = 0; pointIndex < mNumPoints; ++pointIndex) + { + Vector3 randVec = RandomVector3(&mRNG); + mTranslateData.push_back(randVec); + } + + mSizeData.resize(mNumPoints, 0.012f); + mColorData.resize(mNumPoints, Vector3(1.0f, 0.0f, 0.0f)); + } + + mBuffers.resize(5, 0); + glGenBuffers(static_cast(mBuffers.size()), &mBuffers[0]); + + // Index Data + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBuffers[0]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, VectorSizeBytes(indexData), &indexData[0], + GL_STATIC_DRAW); + + // Position Data + glBindBuffer(GL_ARRAY_BUFFER, mBuffers[1]); + glBufferData(GL_ARRAY_BUFFER, VectorSizeBytes(positionData), &positionData[0], GL_STATIC_DRAW); + GLint positionLocation = glGetAttribLocation(mProgram, "aPosition"); + ASSERT_NE(-1, positionLocation); + glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 8, nullptr); + glEnableVertexAttribArray(positionLocation); + + // Translate Data + glBindBuffer(GL_ARRAY_BUFFER, mBuffers[2]); + glBufferData(GL_ARRAY_BUFFER, VectorSizeBytes(mTranslateData), &mTranslateData[0], + GL_STATIC_DRAW); + GLint translateLocation = glGetAttribLocation(mProgram, "aTranslate"); + ASSERT_NE(-1, translateLocation); + glVertexAttribPointer(translateLocation, 3, GL_FLOAT, GL_FALSE, 12, nullptr); + glEnableVertexAttribArray(translateLocation); + glVertexAttribDivisorANGLE(translateLocation, 1); + + // Scale Data + glBindBuffer(GL_ARRAY_BUFFER, mBuffers[3]); + glBufferData(GL_ARRAY_BUFFER, VectorSizeBytes(mSizeData), nullptr, GL_DYNAMIC_DRAW); + GLint scaleLocation = glGetAttribLocation(mProgram, "aScale"); + ASSERT_NE(-1, scaleLocation); + glVertexAttribPointer(scaleLocation, 1, GL_FLOAT, GL_FALSE, 4, nullptr); + glEnableVertexAttribArray(scaleLocation); + glVertexAttribDivisorANGLE(scaleLocation, 1); + + // Color Data + glBindBuffer(GL_ARRAY_BUFFER, mBuffers[4]); + glBufferData(GL_ARRAY_BUFFER, VectorSizeBytes(mColorData), nullptr, GL_DYNAMIC_DRAW); + GLint colorLocation = glGetAttribLocation(mProgram, "aColor"); + ASSERT_NE(-1, colorLocation); + glVertexAttribPointer(colorLocation, 3, GL_FLOAT, GL_FALSE, 12, nullptr); + glEnableVertexAttribArray(colorLocation); + glVertexAttribDivisorANGLE(colorLocation, 1); + + // Set the viewport + glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight()); + + // Init matrices + GLint worldMatrixLocation = glGetUniformLocation(mProgram, "uWorldMatrix"); + ASSERT_NE(-1, worldMatrixLocation); + Matrix4 worldMatrix = Matrix4::translate(Vector3(0, 0, -3.0f)); + worldMatrix *= Matrix4::rotate(25.0f, Vector3(0.6f, 1.0f, 0.0f)); + glUniformMatrix4fv(worldMatrixLocation, 1, GL_FALSE, &worldMatrix.data[0]); + + GLint projectionMatrixLocation = glGetUniformLocation(mProgram, "uProjectionMatrix"); + ASSERT_NE(-1, projectionMatrixLocation); + float fov = + static_cast(getWindow()->getWidth()) / static_cast(getWindow()->getHeight()); + Matrix4 projectionMatrix = Matrix4::perspective(60.0f, fov, 1.0f, 300.0f); + glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE, &projectionMatrix.data[0]); + + getWindow()->setVisible(true); + + ASSERT_GL_NO_ERROR(); +} + +void InstancingPerfBenchmark::destroyBenchmark() +{ + glDeleteProgram(mProgram); + + if (!mBuffers.empty()) + { + glDeleteBuffers(static_cast(mBuffers.size()), &mBuffers[0]); + mBuffers.clear(); + } +} + +void InstancingPerfBenchmark::drawBenchmark() +{ + glClear(GL_COLOR_BUFFER_BIT); + + const auto ¶ms = GetParam(); + + // Animatino makes the test more interesting visually, but also eats up many CPU cycles. + if (params.animationEnabled) + { + // Not implemented for billboards. + ASSERT(params.instancingEnabled); + + float time = static_cast(mTimer->getElapsedTime()); + + for (size_t pointIndex = 0; pointIndex < mTranslateData.size(); ++pointIndex) + { + const Vector3 &translate = mTranslateData[pointIndex]; + + float tx = translate.x + time; + float ty = translate.y + time; + float tz = translate.z + time; + + float scale = AnimationSignal(tx) * 0.01f + 0.01f; + mSizeData[pointIndex] = scale; + + Vector3 color; + color.x = AnimationSignal(tx) * 0.5f + 0.5f; + color.y = AnimationSignal(ty) * 0.5f + 0.5f; + color.z = AnimationSignal(tz) * 0.5f + 0.5f; + + mColorData[pointIndex] = color; + } + } + + // Update scales and colors. + glBindBuffer(GL_ARRAY_BUFFER, mBuffers[3]); + glBufferSubData(GL_ARRAY_BUFFER, 0, VectorSizeBytes(mSizeData), &mSizeData[0]); + + glBindBuffer(GL_ARRAY_BUFFER, mBuffers[4]); + glBufferSubData(GL_ARRAY_BUFFER, 0, VectorSizeBytes(mColorData), &mColorData[0]); + + // Render the instances/billboards. + if (params.instancingEnabled) + { + for (unsigned int it = 0; it < params.iterations; it++) + { + glDrawElementsInstancedANGLE(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr, mNumPoints); + } + } + else + { + for (unsigned int it = 0; it < params.iterations; it++) + { + glDrawElements(GL_TRIANGLES, 6 * mNumPoints, GL_UNSIGNED_INT, nullptr); + } + } + + ASSERT_GL_NO_ERROR(); +} + +InstancingPerfParams InstancingPerfD3D11Params() +{ + InstancingPerfParams params; + params.eglParameters = D3D11(); + return params; +} + +InstancingPerfParams InstancingPerfD3D9Params() +{ + InstancingPerfParams params; + params.eglParameters = D3D9(); + return params; +} + +InstancingPerfParams InstancingPerfOpenGLParams() +{ + InstancingPerfParams params; + params.eglParameters = OPENGL(); + return params; +} + +TEST_P(InstancingPerfBenchmark, Run) +{ + run(); +} + +ANGLE_INSTANTIATE_TEST(InstancingPerfBenchmark, + InstancingPerfD3D11Params(), + InstancingPerfD3D9Params(), + InstancingPerfOpenGLParams()); + +} // anonymous namespace diff --git a/gfx/angle/src/tests/perf_tests/InterleavedAttributeData.cpp b/gfx/angle/src/tests/perf_tests/InterleavedAttributeData.cpp index d0f09df9d0..15e2a5b14b 100644 --- a/gfx/angle/src/tests/perf_tests/InterleavedAttributeData.cpp +++ b/gfx/angle/src/tests/perf_tests/InterleavedAttributeData.cpp @@ -54,7 +54,6 @@ class InterleavedAttributeDataBenchmark void initializeBenchmark() override; void destroyBenchmark() override; - void beginDrawBenchmark() override; void drawBenchmark() override; private: @@ -95,7 +94,7 @@ void InterleavedAttributeDataBenchmark::initializeBenchmark() "}"; mPointSpriteProgram = CompileProgram(vs, fs); - ASSERT_TRUE(mPointSpriteProgram != 0); + ASSERT_NE(0u, mPointSpriteProgram); glClearColor(0.0f, 1.0f, 0.0f, 1.0f); @@ -149,15 +148,11 @@ void InterleavedAttributeDataBenchmark::destroyBenchmark() } } -void InterleavedAttributeDataBenchmark::beginDrawBenchmark() -{ - // Clear the color buffer - glClear(GL_COLOR_BUFFER_BIT); -} - void InterleavedAttributeDataBenchmark::drawBenchmark() { - for (size_t k = 0; k < 4; k++) + glClear(GL_COLOR_BUFFER_BIT); + + for (size_t k = 0; k < 20; k++) { for (size_t i = 0; i < ArraySize(mPositionColorBuffer); i++) { diff --git a/gfx/angle/src/tests/perf_tests/PointSprites.cpp b/gfx/angle/src/tests/perf_tests/PointSprites.cpp index 4202b46ee8..b0b23109b3 100644 --- a/gfx/angle/src/tests/perf_tests/PointSprites.cpp +++ b/gfx/angle/src/tests/perf_tests/PointSprites.cpp @@ -29,7 +29,7 @@ struct PointSpritesParams final : public RenderTestParams minorVersion = 0; windowWidth = 1280; windowHeight = 720; - iterations = 10; + iterations = 100; count = 10; size = 3.0f; numVaryings = 3; @@ -59,12 +59,12 @@ class PointSpritesBenchmark : public ANGLERenderTest, void initializeBenchmark() override; void destroyBenchmark() override; - void beginDrawBenchmark() override; void drawBenchmark() override; private: GLuint mProgram; GLuint mBuffer; + RNG mRNG; }; std::string PointSpritesParams::suffix() const @@ -79,7 +79,7 @@ std::string PointSpritesParams::suffix() const } PointSpritesBenchmark::PointSpritesBenchmark() - : ANGLERenderTest("PointSprites", GetParam()) + : ANGLERenderTest("PointSprites", GetParam()), mRNG(1) { } @@ -87,8 +87,7 @@ void PointSpritesBenchmark::initializeBenchmark() { const auto ¶ms = GetParam(); - mDrawIterations = params.iterations; - ASSERT_TRUE(params.iterations > 0); + ASSERT_LT(0u, params.iterations); std::stringstream vstrstr; @@ -144,7 +143,7 @@ void PointSpritesBenchmark::initializeBenchmark() "}\n"; mProgram = CompileProgram(vstrstr.str(), fstrstr.str()); - ASSERT_TRUE(mProgram != 0); + ASSERT_NE(0u, mProgram); // Use the program object glUseProgram(mProgram); @@ -154,24 +153,24 @@ void PointSpritesBenchmark::initializeBenchmark() std::vector vertexPositions(params.count * 2); for (size_t pointIndex = 0; pointIndex < vertexPositions.size(); ++pointIndex) { - vertexPositions[pointIndex] = RandomBetween(-1.0f, 1.0f); + vertexPositions[pointIndex] = mRNG.randomNegativeOneToOne(); } glGenBuffers(1, &mBuffer); glBindBuffer(GL_ARRAY_BUFFER, mBuffer); glBufferData(GL_ARRAY_BUFFER, vertexPositions.size() * sizeof(float), &vertexPositions[0], GL_STATIC_DRAW); - int positionLocation = glGetAttribLocation(mProgram, "vPosition"); - ASSERT_TRUE(positionLocation != -1); + GLint positionLocation = glGetAttribLocation(mProgram, "vPosition"); + ASSERT_NE(-1, positionLocation); - glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); + glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, nullptr); glEnableVertexAttribArray(positionLocation); // Set the viewport glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight()); - int pointSizeLocation = glGetUniformLocation(mProgram, "uPointSize"); - ASSERT_TRUE(pointSizeLocation != -1); + GLint pointSizeLocation = glGetUniformLocation(mProgram, "uPointSize"); + ASSERT_NE(-1, pointSizeLocation); glUniform1f(pointSizeLocation, params.size); @@ -184,14 +183,10 @@ void PointSpritesBenchmark::destroyBenchmark() glDeleteBuffers(1, &mBuffer); } -void PointSpritesBenchmark::beginDrawBenchmark() -{ - // Clear the color buffer - glClear(GL_COLOR_BUFFER_BIT); -} - void PointSpritesBenchmark::drawBenchmark() { + glClear(GL_COLOR_BUFFER_BIT); + const auto ¶ms = GetParam(); for (unsigned int it = 0; it < params.iterations; it++) diff --git a/gfx/angle/src/tests/perf_tests/TexSubImage.cpp b/gfx/angle/src/tests/perf_tests/TexSubImage.cpp index ed42bc0cd5..3e4b5cf687 100644 --- a/gfx/angle/src/tests/perf_tests/TexSubImage.cpp +++ b/gfx/angle/src/tests/perf_tests/TexSubImage.cpp @@ -31,7 +31,7 @@ struct TexSubImageParams final : public RenderTestParams imageHeight = 1024; subImageWidth = 64; subImageHeight = 64; - iterations = 3; + iterations = 9; } std::string suffix() const override; @@ -58,7 +58,6 @@ class TexSubImageBenchmark : public ANGLERenderTest, void initializeBenchmark() override; void destroyBenchmark() override; - void beginDrawBenchmark() override; void drawBenchmark() override; private: @@ -99,7 +98,7 @@ TexSubImageBenchmark::TexSubImageBenchmark() mTexture(0), mVertexBuffer(0), mIndexBuffer(0), - mPixels(NULL) + mPixels(nullptr) { } @@ -108,7 +107,6 @@ GLuint TexSubImageBenchmark::createTexture() const auto ¶ms = GetParam(); assert(params.iterations > 0); - mDrawIterations = params.iterations; // Use tightly packed data glPixelStorei(GL_UNPACK_ALIGNMENT, 1); @@ -157,7 +155,7 @@ void TexSubImageBenchmark::initializeBenchmark() ); mProgram = CompileProgram(vs, fs); - ASSERT_TRUE(mProgram != 0); + ASSERT_NE(0u, mProgram); // Get the attribute locations mPositionLoc = glGetAttribLocation(mProgram, "a_position"); @@ -220,7 +218,7 @@ void TexSubImageBenchmark::destroyBenchmark() delete[] mPixels; } -void TexSubImageBenchmark::beginDrawBenchmark() +void TexSubImageBenchmark::drawBenchmark() { // Set the viewport glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight()); @@ -251,10 +249,7 @@ void TexSubImageBenchmark::beginDrawBenchmark() glUniform1i(mSamplerLoc, 0); ASSERT_GL_NO_ERROR(); -} -void TexSubImageBenchmark::drawBenchmark() -{ const auto ¶ms = GetParam(); for (unsigned int iteration = 0; iteration < params.iterations; ++iteration) diff --git a/gfx/angle/src/tests/perf_tests/TextureSampling.cpp b/gfx/angle/src/tests/perf_tests/TextureSampling.cpp new file mode 100644 index 0000000000..bc00acddfc --- /dev/null +++ b/gfx/angle/src/tests/perf_tests/TextureSampling.cpp @@ -0,0 +1,288 @@ +// +// Copyright (c) 2015 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. +// +// TextureSamplingBenchmark: +// Performance test for texture sampling. The test generates a texture containing random data +// and then blurs it in a fragment shader using nearest neighbor sampling. The test is +// specifically designed to test overhead of GLSL's builtin texture*() functions that may result +// from how ANGLE translates them on each backend. +// + +#include "ANGLEPerfTest.h" + +#include +#include +#include + +#include "shader_utils.h" + +using namespace angle; + +namespace +{ + +struct TextureSamplingParams final : public RenderTestParams +{ + TextureSamplingParams() + { + // Common default params + majorVersion = 2; + minorVersion = 0; + windowWidth = 720; + windowHeight = 720; + iterations = 4; + + numSamplers = 2; + textureSize = 32; + kernelSize = 3; + } + + std::string suffix() const override; + unsigned int numSamplers; + unsigned int textureSize; + unsigned int kernelSize; + + // static parameters + unsigned int iterations; +}; + +std::ostream &operator<<(std::ostream &os, const TextureSamplingParams ¶ms) +{ + os << params.suffix().substr(1); + return os; +} + +std::string TextureSamplingParams::suffix() const +{ + std::stringstream strstr; + + strstr << RenderTestParams::suffix() << "_" << numSamplers << "samplers"; + + return strstr.str(); +} + +class TextureSamplingBenchmark : public ANGLERenderTest, + public ::testing::WithParamInterface +{ + public: + TextureSamplingBenchmark(); + + void initializeBenchmark() override; + void destroyBenchmark() override; + void drawBenchmark() override; + + private: + void initShaders(); + void initVertexBuffer(); + void initTextures(); + + GLuint mProgram; + GLuint mBuffer; + std::vector mTextures; +}; + +TextureSamplingBenchmark::TextureSamplingBenchmark() + : ANGLERenderTest("TextureSampling", GetParam()), mProgram(0u), mBuffer(0u) +{ +} + +void TextureSamplingBenchmark::initializeBenchmark() +{ + const auto ¶ms = GetParam(); + + ASSERT_LT(0u, params.iterations); + + // Verify "numSamplers" is within MAX_TEXTURE_IMAGE_UNITS limit + GLint maxTextureImageUnits; + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits); + + if (params.numSamplers > static_cast(maxTextureImageUnits)) + { + FAIL() << "Sampler count (" << params.numSamplers << ")" + << " exceeds maximum texture count: " << maxTextureImageUnits << std::endl; + } + initShaders(); + initVertexBuffer(); + initTextures(); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight()); + + ASSERT_GL_NO_ERROR(); +} + +void TextureSamplingBenchmark::initShaders() +{ + const auto ¶ms = GetParam(); + + std::stringstream vstrstr; + vstrstr << "attribute vec2 aPosition;\n" + "varying vec2 vTextureCoordinates;\n" + "void main()\n" + "{\n" + " vTextureCoordinates = (aPosition + vec2(1.0)) * 0.5;\n" + " gl_Position = vec4(aPosition, 0, 1.0);\n" + "}"; + + std::stringstream fstrstr; + fstrstr << "precision mediump float;\n" + "varying vec2 vTextureCoordinates;\n"; + for (unsigned int count = 0; count < params.numSamplers; count++) + { + fstrstr << "uniform sampler2D uSampler" << count << ";\n"; + } + fstrstr << "void main()\n" + "{\n" + " const float inverseTextureSize = 1.0 / " + << params.textureSize << ".0;\n" + " vec4 colorOut = vec4(0.0, 0.0, 0.0, 1.0);\n"; + for (unsigned int count = 0; count < params.numSamplers; count++) + { + fstrstr << " for (int x = 0; x < " << params.kernelSize << "; ++x)\n" + " {\n" + " for (int y = 0; y < " << params.kernelSize << "; ++y)\n" + " {\n" + " colorOut += texture2D(uSampler" << count + << ", vTextureCoordinates + vec2(x, y) * inverseTextureSize) * 0.1;\n" + " }\n" + " }\n"; + } + fstrstr << " gl_FragColor = colorOut;\n" + "}\n"; + + mProgram = CompileProgram(vstrstr.str(), fstrstr.str()); + ASSERT_NE(0u, mProgram); + + // Use the program object + glUseProgram(mProgram); +} + +void TextureSamplingBenchmark::initVertexBuffer() +{ + std::vector vertexPositions(12); + { + // Bottom left triangle + vertexPositions[0] = -1.0f; + vertexPositions[1] = -1.0f; + vertexPositions[2] = 1.0f; + vertexPositions[3] = -1.0f; + vertexPositions[4] = -1.0f; + vertexPositions[5] = 1.0f; + + // Top right triangle + vertexPositions[6] = -1.0f; + vertexPositions[7] = 1.0f; + vertexPositions[8] = 1.0f; + vertexPositions[9] = -1.0f; + vertexPositions[10] = 1.0f; + vertexPositions[11] = 1.0f; + } + + glGenBuffers(1, &mBuffer); + glBindBuffer(GL_ARRAY_BUFFER, mBuffer); + glBufferData(GL_ARRAY_BUFFER, vertexPositions.size() * sizeof(float), &vertexPositions[0], + GL_STATIC_DRAW); + + GLint positionLocation = glGetAttribLocation(mProgram, "aPosition"); + ASSERT_NE(-1, positionLocation); + + glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + glEnableVertexAttribArray(positionLocation); +} + +void TextureSamplingBenchmark::initTextures() +{ + const auto ¶ms = GetParam(); + + unsigned int dataSize = params.textureSize * params.textureSize; + std::vector randomTextureData; + randomTextureData.resize(dataSize); + + unsigned int pseudoRandom = 1u; + for (unsigned int i = 0; i < dataSize; ++i) + { + pseudoRandom = pseudoRandom * 1664525u + 1013904223u; + randomTextureData[i] = pseudoRandom; + } + + mTextures.resize(params.numSamplers); + glGenTextures(params.numSamplers, mTextures.data()); + for (unsigned int i = 0; i < params.numSamplers; ++i) + { + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, mTextures[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, params.textureSize, params.textureSize, 0, GL_RGBA, + GL_UNSIGNED_BYTE, randomTextureData.data()); + } + + for (unsigned int count = 0; count < params.numSamplers; count++) + { + std::stringstream samplerstrstr; + samplerstrstr << "uSampler" << count; + GLint samplerLocation = glGetUniformLocation(mProgram, samplerstrstr.str().c_str()); + ASSERT_NE(-1, samplerLocation); + + glUniform1i(samplerLocation, count); + } +} + +void TextureSamplingBenchmark::destroyBenchmark() +{ + const auto ¶ms = GetParam(); + + glDeleteProgram(mProgram); + glDeleteBuffers(1, &mBuffer); + if (!mTextures.empty()) + { + glDeleteTextures(params.numSamplers, mTextures.data()); + } +} + +void TextureSamplingBenchmark::drawBenchmark() +{ + glClear(GL_COLOR_BUFFER_BIT); + + const auto ¶ms = GetParam(); + + for (unsigned int it = 0; it < params.iterations; ++it) + { + glDrawArrays(GL_TRIANGLES, 0, 6); + } + + ASSERT_GL_NO_ERROR(); +} + +TextureSamplingParams D3D11Params() +{ + TextureSamplingParams params; + params.eglParameters = egl_platform::D3D11(); + return params; +} + +TextureSamplingParams D3D9Params() +{ + TextureSamplingParams params; + params.eglParameters = egl_platform::D3D9(); + return params; +} + +TextureSamplingParams OpenGLParams() +{ + TextureSamplingParams params; + params.eglParameters = egl_platform::OPENGL(); + return params; +} + +} // anonymous namespace + +TEST_P(TextureSamplingBenchmark, Run) +{ + run(); +} + +ANGLE_INSTANTIATE_TEST(TextureSamplingBenchmark, D3D11Params(), D3D9Params(), OpenGLParams()); diff --git a/gfx/angle/src/tests/test_utils/ANGLETest.cpp b/gfx/angle/src/tests/test_utils/ANGLETest.cpp index a421bfc46e..74969f940a 100644 --- a/gfx/angle/src/tests/test_utils/ANGLETest.cpp +++ b/gfx/angle/src/tests/test_utils/ANGLETest.cpp @@ -1,11 +1,58 @@ +// +// Copyright 2015 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. +// +// ANGLETest: +// Implementation of common ANGLE testing fixture. +// + #include "ANGLETest.h" #include "EGLWindow.h" #include "OSWindow.h" +#include "system_utils.h" + +namespace angle +{ + +GLColor::GLColor() : R(0), G(0), B(0), A(0) +{ +} + +GLColor::GLColor(GLubyte r, GLubyte g, GLubyte b, GLubyte a) : R(r), G(g), B(b), A(a) +{ +} + +GLColor::GLColor(GLuint colorValue) : R(0), G(0), B(0), A(0) +{ + memcpy(&R, &colorValue, sizeof(GLuint)); +} + +GLColor ReadColor(GLint x, GLint y) +{ + GLColor actual; + glReadPixels((x), (y), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &actual.R); + EXPECT_GL_NO_ERROR(); + return actual; +} + +bool operator==(const GLColor &a, const GLColor &b) +{ + return a.R == b.R && a.G == b.G && a.B == b.B && a.A == b.A; +} + +std::ostream &operator<<(std::ostream &ostream, const GLColor &color) +{ + ostream << "(" << static_cast(color.R) << ", " + << static_cast(color.G) << ", " << static_cast(color.B) + << ", " << static_cast(color.A) << ")"; + return ostream; +} + +} // namespace angle ANGLETest::ANGLETest() - : mEGLWindow(nullptr), - mWidth(16), - mHeight(16) + : mEGLWindow(nullptr), mWidth(16), mHeight(16), mIgnoreD3D11SDKLayersWarnings(false) { mEGLWindow = new EGLWindow(GetParam().majorVersion, GetParam().minorVersion, GetParam().eglParameters); @@ -47,10 +94,18 @@ void ANGLETest::SetUp() // taking OpenGL traces can guess the size of the default framebuffer and show it // in their UIs glViewport(0, 0, mWidth, mHeight); + + const auto &info = testing::UnitTest::GetInstance()->current_test_info(); + angle::WriteDebugMessage("Entering %s.%s\n", info->test_case_name(), info->name()); } void ANGLETest::TearDown() { + checkD3D11SDKLayersMessages(); + + const auto &info = testing::UnitTest::GetInstance()->current_test_info(); + angle::WriteDebugMessage("Exiting %s.%s\n", info->test_case_name(), info->name()); + swapBuffers(); mOSWindow->messageLoop(); @@ -78,21 +133,30 @@ void ANGLETest::swapBuffers() } } -void ANGLETest::drawQuad(GLuint program, const std::string& positionAttribName, GLfloat quadDepth, GLfloat quadScale) +void ANGLETest::drawQuad(GLuint program, + const std::string &positionAttribName, + GLfloat positionAttribZ) +{ + drawQuad(program, positionAttribName, positionAttribZ, 1.0f); +} + +void ANGLETest::drawQuad(GLuint program, + const std::string &positionAttribName, + GLfloat positionAttribZ, + GLfloat positionAttribXYScale) { GLint positionLocation = glGetAttribLocation(program, positionAttribName.c_str()); glUseProgram(program); - const GLfloat vertices[] = - { - -1.0f * quadScale, 1.0f * quadScale, quadDepth, - -1.0f * quadScale, -1.0f * quadScale, quadDepth, - 1.0f * quadScale, -1.0f * quadScale, quadDepth, + const GLfloat vertices[] = { + -1.0f * positionAttribXYScale, 1.0f * positionAttribXYScale, positionAttribZ, + -1.0f * positionAttribXYScale, -1.0f * positionAttribXYScale, positionAttribZ, + 1.0f * positionAttribXYScale, -1.0f * positionAttribXYScale, positionAttribZ, - -1.0f * quadScale, 1.0f * quadScale, quadDepth, - 1.0f * quadScale, -1.0f * quadScale, quadDepth, - 1.0f * quadScale, 1.0f * quadScale, quadDepth, + -1.0f * positionAttribXYScale, 1.0f * positionAttribXYScale, positionAttribZ, + 1.0f * positionAttribXYScale, -1.0f * positionAttribXYScale, positionAttribZ, + 1.0f * positionAttribXYScale, 1.0f * positionAttribXYScale, positionAttribZ, }; glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices); @@ -141,6 +205,79 @@ GLuint ANGLETest::compileShader(GLenum type, const std::string &source) return shader; } +void ANGLETest::checkD3D11SDKLayersMessages() +{ +#if defined(ANGLE_PLATFORM_WINDOWS) && !defined(NDEBUG) + // In debug D3D11 mode, check ID3D11InfoQueue to see if any D3D11 SDK Layers messages + // were outputted by the test + if (mIgnoreD3D11SDKLayersWarnings || + mEGLWindow->getPlatform().renderer != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE || + mEGLWindow->getDisplay() == EGL_NO_DISPLAY) + { + return; + } + + const char *extensionString = + static_cast(eglQueryString(mEGLWindow->getDisplay(), EGL_EXTENSIONS)); + if (!strstr(extensionString, "EGL_EXT_device_query")) + { + return; + } + + EGLAttrib device = 0; + EGLAttrib angleDevice = 0; + + PFNEGLQUERYDISPLAYATTRIBEXTPROC queryDisplayAttribEXT; + PFNEGLQUERYDEVICEATTRIBEXTPROC queryDeviceAttribEXT; + + queryDisplayAttribEXT = reinterpret_cast( + eglGetProcAddress("eglQueryDisplayAttribEXT")); + queryDeviceAttribEXT = reinterpret_cast( + eglGetProcAddress("eglQueryDeviceAttribEXT")); + ASSERT_NE(nullptr, queryDisplayAttribEXT); + ASSERT_NE(nullptr, queryDeviceAttribEXT); + + ASSERT_EGL_TRUE(queryDisplayAttribEXT(mEGLWindow->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); + ASSERT_EGL_TRUE(queryDeviceAttribEXT(reinterpret_cast(angleDevice), + EGL_D3D11_DEVICE_ANGLE, &device)); + ID3D11Device *d3d11Device = reinterpret_cast(device); + + ID3D11InfoQueue *infoQueue = nullptr; + HRESULT hr = + d3d11Device->QueryInterface(__uuidof(infoQueue), reinterpret_cast(&infoQueue)); + if (SUCCEEDED(hr)) + { + UINT64 numStoredD3DDebugMessages = + infoQueue->GetNumStoredMessagesAllowedByRetrievalFilter(); + + if (numStoredD3DDebugMessages > 0) + { + for (UINT64 i = 0; i < numStoredD3DDebugMessages; i++) + { + SIZE_T messageLength = 0; + hr = infoQueue->GetMessage(i, nullptr, &messageLength); + + if (SUCCEEDED(hr)) + { + D3D11_MESSAGE *pMessage = + reinterpret_cast(malloc(messageLength)); + infoQueue->GetMessage(i, pMessage, &messageLength); + + std::cout << "Message " << i << ":" + << " " << pMessage->pDescription << "\n"; + free(pMessage); + } + } + + FAIL() << numStoredD3DDebugMessages + << " D3D11 SDK Layers message(s) detected! Test Failed.\n"; + } + } + + SafeRelease(infoQueue); +#endif +} + static bool checkExtensionExists(const char *allExtensions, const std::string &extName) { return strstr(allExtensions, extName.c_str()) != nullptr; @@ -207,6 +344,16 @@ void ANGLETest::setMultisampleEnabled(bool enabled) mEGLWindow->setMultisample(enabled); } +void ANGLETest::setDebugEnabled(bool enabled) +{ + mEGLWindow->setDebugEnabled(enabled); +} + +void ANGLETest::setNoErrorEnabled(bool enabled) +{ + mEGLWindow->setNoErrorEnabled(enabled); +} + int ANGLETest::getClientVersion() const { return mEGLWindow->getClientMajorVersion(); @@ -316,12 +463,27 @@ bool ANGLETest::isD3DSM3() const return isD3D9() || isD3D11_FL93(); } +bool ANGLETest::isOSX() const +{ +#ifdef __APPLE__ + return true; +#else + return false; +#endif +} + EGLint ANGLETest::getPlatformRenderer() const { assert(mEGLWindow); return mEGLWindow->getPlatform().renderer; } +void ANGLETest::ignoreD3D11SDKLayersWarnings() +{ + // Some tests may need to disable the D3D11 SDK Layers Warnings checks + mIgnoreD3D11SDKLayersWarnings = true; +} + OSWindow *ANGLETest::mOSWindow = NULL; void ANGLETestEnvironment::SetUp() diff --git a/gfx/angle/src/tests/test_utils/ANGLETest.h b/gfx/angle/src/tests/test_utils/ANGLETest.h index d653615c29..9dc7449667 100644 --- a/gfx/angle/src/tests/test_utils/ANGLETest.h +++ b/gfx/angle/src/tests/test_utils/ANGLETest.h @@ -3,6 +3,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // +// ANGLETest: +// Implementation of common ANGLE testing fixture. +// #ifndef ANGLE_TESTS_ANGLE_TEST_H_ #define ANGLE_TESTS_ANGLE_TEST_H_ @@ -16,31 +19,57 @@ #include "shader_utils.h" #define EXPECT_GL_ERROR(err) EXPECT_EQ(static_cast(err), glGetError()) -#define EXPECT_GL_NO_ERROR() EXPECT_GL_ERROR(GL_NO_ERROR) +#define EXPECT_GL_NO_ERROR() EXPECT_EQ(static_cast(GL_NO_ERROR), glGetError()) #define ASSERT_GL_ERROR(err) ASSERT_EQ(static_cast(err), glGetError()) -#define ASSERT_GL_NO_ERROR() ASSERT_GL_ERROR(GL_NO_ERROR) +#define ASSERT_GL_NO_ERROR() ASSERT_EQ(static_cast(GL_NO_ERROR), glGetError()) #define EXPECT_EGL_ERROR(err) EXPECT_EQ((err), eglGetError()) #define EXPECT_EGL_SUCCESS() EXPECT_EGL_ERROR(EGL_SUCCESS) +// EGLBoolean is |unsigned int| but EGL_TRUE is 0, not 0u. +#define ASSERT_EGL_TRUE(a) ASSERT_EQ(static_cast(EGL_TRUE), (a)) +#define ASSERT_EGL_FALSE(a) ASSERT_EQ(static_cast(EGL_FALSE), (a)) +#define EXPECT_EGL_TRUE(a) EXPECT_EQ(static_cast(EGL_TRUE), (a)) +#define EXPECT_EGL_FALSE(a) EXPECT_EQ(static_cast(EGL_FALSE), (a)) + #define ASSERT_EGL_ERROR(err) ASSERT_EQ((err), eglGetError()) #define ASSERT_EGL_SUCCESS() ASSERT_EGL_ERROR(EGL_SUCCESS) #define ASSERT_GLENUM_EQ(expected, actual) ASSERT_EQ(static_cast(expected), static_cast(actual)) #define EXPECT_GLENUM_EQ(expected, actual) EXPECT_EQ(static_cast(expected), static_cast(actual)) -#define EXPECT_PIXEL_EQ(x, y, r, g, b, a) \ -{ \ - GLubyte pixel[4]; \ - glReadPixels((x), (y), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); \ - EXPECT_GL_NO_ERROR(); \ - EXPECT_EQ((r), pixel[0]); \ - EXPECT_EQ((g), pixel[1]); \ - EXPECT_EQ((b), pixel[2]); \ - EXPECT_EQ((a), pixel[3]); \ +namespace angle +{ +struct GLColor +{ + GLColor(); + GLColor(GLubyte r, GLubyte g, GLubyte b, GLubyte a); + GLColor(GLuint colorValue); + + GLubyte R, G, B, A; +}; + +// Useful to cast any type to GLubyte. +template +GLColor MakeGLColor(TR r, TG g, TB b, TA a) +{ + return GLColor(static_cast(r), static_cast(g), static_cast(b), + static_cast(a)); } +bool operator==(const GLColor &a, const GLColor &b); +std::ostream &operator<<(std::ostream &ostream, const GLColor &color); +GLColor ReadColor(GLint x, GLint y); + +} // namespace angle + +#define EXPECT_PIXEL_EQ(x, y, r, g, b, a) \ + EXPECT_EQ(angle::MakeGLColor(r, g, b, a), angle::ReadColor(x, y)) + +#define EXPECT_PIXEL_COLOR_EQ(x, y, angleColor) EXPECT_EQ(angleColor, angle::ReadColor(x, y)) + +// TODO(jmadill): Figure out how we can use GLColor's nice printing with EXPECT_NEAR. #define EXPECT_PIXEL_NEAR(x, y, r, g, b, a, abs_error) \ { \ GLubyte pixel[4]; \ @@ -73,7 +102,13 @@ class ANGLETest : public ::testing::TestWithParam virtual void swapBuffers(); - static void drawQuad(GLuint program, const std::string& positionAttribName, GLfloat quadDepth, GLfloat quadScale = 1.0f); + static void drawQuad(GLuint program, + const std::string &positionAttribName, + GLfloat positionAttribZ); + static void drawQuad(GLuint program, + const std::string &positionAttribName, + GLfloat positionAttribZ, + GLfloat positionAttribXYScale); static GLuint compileShader(GLenum type, const std::string &source); static bool extensionEnabled(const std::string &extName); static bool eglClientExtensionEnabled(const std::string &extName); @@ -87,6 +122,8 @@ class ANGLETest : public ::testing::TestWithParam void setConfigDepthBits(int bits); void setConfigStencilBits(int bits); void setMultisampleEnabled(bool enabled); + void setDebugEnabled(bool enabled); + void setNoErrorEnabled(bool enabled); int getClientVersion() const; @@ -105,16 +142,23 @@ class ANGLETest : public ::testing::TestWithParam bool isD3D9() const; // Is D3D9 or SM9_3 renderer. bool isD3DSM3() const; + bool isOSX() const; EGLint getPlatformRenderer() const; + void ignoreD3D11SDKLayersWarnings(); + private: bool createEGLContext(); bool destroyEGLContext(); + void checkD3D11SDKLayersMessages(); + EGLWindow *mEGLWindow; int mWidth; int mHeight; + bool mIgnoreD3D11SDKLayersWarnings; + static OSWindow *mOSWindow; }; diff --git a/gfx/angle/src/tests/test_utils/angle_test_configs.cpp b/gfx/angle/src/tests/test_utils/angle_test_configs.cpp index 5ac3358b10..0ed7a74b6e 100644 --- a/gfx/angle/src/tests/test_utils/angle_test_configs.cpp +++ b/gfx/angle/src/tests/test_utils/angle_test_configs.cpp @@ -69,7 +69,7 @@ std::ostream &operator<<(std::ostream& stream, const PlatformParameters &pp) stream << "OPENGL"; break; case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE: - stream << "GLES"; + stream << "OPENGLES"; break; case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE: stream << "DEFAULT"; @@ -113,6 +113,25 @@ std::ostream &operator<<(std::ostream& stream, const PlatformParameters &pp) break; } + switch (pp.eglParameters.presentPath) + { + case EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE: + stream << "_PRESENT_PATH_COPY"; + break; + + case EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE: + stream << "_PRESENT_PATH_FAST"; + break; + + case EGL_DONT_CARE: + // default + break; + + default: + UNREACHABLE(); + break; + } + return stream; } @@ -165,6 +184,12 @@ EGLPlatformParameters D3D11() EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE); } +EGLPlatformParameters D3D11(EGLenum presentPath) +{ + return EGLPlatformParameters(EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, EGL_DONT_CARE, EGL_DONT_CARE, + EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE, presentPath); +} + EGLPlatformParameters D3D11_FL11_1() { return EGLPlatformParameters( @@ -336,6 +361,12 @@ EGLPlatformParameters OPENGLES() return EGLPlatformParameters(EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE); } +EGLPlatformParameters OPENGLES(EGLint major, EGLint minor) +{ + return EGLPlatformParameters(EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE, major, minor, + EGL_DONT_CARE); +} + } // namespace egl_platform // ANGLE tests platforms @@ -354,6 +385,11 @@ PlatformParameters ES2_D3D11() return PlatformParameters(2, 0, egl_platform::D3D11()); } +PlatformParameters ES2_D3D11(EGLenum presentPath) +{ + return PlatformParameters(2, 0, egl_platform::D3D11(presentPath)); +} + PlatformParameters ES2_D3D11_FL11_0() { return PlatformParameters(2, 0, egl_platform::D3D11_FL11_0()); @@ -504,11 +540,21 @@ PlatformParameters ES2_OPENGLES() return PlatformParameters(2, 0, egl_platform::OPENGLES()); } +PlatformParameters ES2_OPENGLES(EGLint major, EGLint minor) +{ + return PlatformParameters(2, 0, egl_platform::OPENGLES(major, minor)); +} + PlatformParameters ES3_OPENGLES() { return PlatformParameters(3, 0, egl_platform::OPENGLES()); } +PlatformParameters ES3_OPENGLES(EGLint major, EGLint minor) +{ + return PlatformParameters(3, 0, egl_platform::OPENGLES(major, minor)); +} + PlatformParameters ES2_OPENGL() { return PlatformParameters(2, 0, egl_platform::OPENGL()); diff --git a/gfx/angle/src/tests/test_utils/angle_test_configs.h b/gfx/angle/src/tests/test_utils/angle_test_configs.h index 4aed593eff..a40a7a18bf 100644 --- a/gfx/angle/src/tests/test_utils/angle_test_configs.h +++ b/gfx/angle/src/tests/test_utils/angle_test_configs.h @@ -52,6 +52,7 @@ EGLPlatformParameters D3D9_NULL(); EGLPlatformParameters D3D9_REFERENCE(); EGLPlatformParameters D3D11(); +EGLPlatformParameters D3D11(EGLenum presentPath); EGLPlatformParameters D3D11_FL11_1(); EGLPlatformParameters D3D11_FL11_0(); EGLPlatformParameters D3D11_FL10_1(); @@ -79,6 +80,7 @@ EGLPlatformParameters OPENGL(EGLint major, EGLint minor); EGLPlatformParameters OPENGL_NULL(); EGLPlatformParameters OPENGLES(); +EGLPlatformParameters OPENGLES(EGLint major, EGLint minor); } // namespace egl_platform @@ -87,6 +89,7 @@ PlatformParameters ES2_D3D9(); PlatformParameters ES2_D3D9_REFERENCE(); PlatformParameters ES2_D3D11(); +PlatformParameters ES2_D3D11(EGLenum presentPath); PlatformParameters ES2_D3D11_FL11_0(); PlatformParameters ES2_D3D11_FL10_1(); PlatformParameters ES2_D3D11_FL10_0(); @@ -128,7 +131,9 @@ PlatformParameters ES3_OPENGL(); PlatformParameters ES3_OPENGL(EGLint major, EGLint minor); PlatformParameters ES2_OPENGLES(); +PlatformParameters ES2_OPENGLES(EGLint major, EGLint minor); PlatformParameters ES3_OPENGLES(); +PlatformParameters ES3_OPENGLES(EGLint major, EGLint minor); } // namespace angle diff --git a/gfx/angle/src/tests/tests.gyp b/gfx/angle/src/tests/tests.gyp index e72935db4b..79f7384a58 100644 --- a/gfx/angle/src/tests/tests.gyp +++ b/gfx/angle/src/tests/tests.gyp @@ -11,7 +11,6 @@ 'variables': { 'angle_build_conformance_tests%': '0', - 'angle_build_deqp_tests%': '0', 'rapidjson_include_dir': 'third_party/rapidjson/include', 'rapidjson_headers': @@ -363,71 +362,6 @@ }, ], }], - ['angle_build_deqp_tests', - { - 'targets': - [ - { - 'target_name': 'angle_deqp_tests', - 'type': 'executable', - 'includes': [ '../../build/common_defines.gypi', ], - 'dependencies': - [ - '<(angle_path)/src/angle.gyp:libGLESv2', - '<(angle_path)/src/angle.gyp:libEGL', - 'third_party/deqp/src/deqp/modules/gles3/gles3.gyp:deqp-gles3', - 'third_party/deqp/src/deqp/framework/platform/platform.gyp:tcutil-platform', - 'angle_test_support', - ], - 'include_dirs': - [ - '<(angle_path)/include', - 'deqp_tests', - ], - 'variables': - { - 'deqp_tests_output_dir': '<(SHARED_INTERMEDIATE_DIR)/deqp_tests', - 'deqp_tests_input_file': 'deqp_tests/deqp_tests.txt', - 'deqp_tests_generated_file': '<(deqp_tests_output_dir)/generated_deqp_tests.cpp', - }, - 'sources': - [ - 'deqp_tests/deqp_test_main.cpp', - 'deqp_tests/deqp_tests.cpp', - 'deqp_tests/deqp_tests.h', - '<(deqp_tests_generated_file)', - ], - 'actions': - [ - { - 'action_name': 'generate_deqp_tests', - 'message': 'Generating dEQP tests...', - 'msvs_cygwin_shell': 0, - 'variables': - { - 'deqp_tests_generator_script': 'deqp_tests/generate_deqp_tests.py', - }, - 'inputs': - [ - '<(deqp_tests_generator_script)', - '<(deqp_tests_input_file)', - ], - 'outputs': - [ - '<(deqp_tests_generated_file)', - ], - 'action': - [ - 'python', - '<(deqp_tests_generator_script)', - '<(deqp_tests_input_file)', - '<(deqp_tests_generated_file)', - ], - }, - ], - }, - ], - }], ], }], ], diff --git a/gfx/angle/src/tests/third_party/gpu_test_expectations/HowToMakeChanges.md b/gfx/angle/src/tests/third_party/gpu_test_expectations/HowToMakeChanges.md index 807aba0044..7817d0a50a 100644 --- a/gfx/angle/src/tests/third_party/gpu_test_expectations/HowToMakeChanges.md +++ b/gfx/angle/src/tests/third_party/gpu_test_expectations/HowToMakeChanges.md @@ -15,8 +15,8 @@ How to update from Chromium: * ```git apply -R angle-mods.patch```, ```git add . -u```, ```git commit``` * Copy over Chromium files, ```git add . -u```, ```git commit``` - * ```git revert HEAD~2``` + * ```git revert HEAD~``` * ```rm angle-mods.patch``` - * ```git diff HEAD~1 (`)ls(`) > angle-mods.patch```,```git add angle-mods.patch```, ```git commit --amend``` + * ```git diff HEAD~ (`)ls(`) > angle-mods.patch```,```git add angle-mods.patch```, ```git commit --amend``` * ```git rebase -i``` to squash the three patches into one. diff --git a/gfx/angle/src/tests/third_party/gpu_test_expectations/angle-mods.patch b/gfx/angle/src/tests/third_party/gpu_test_expectations/angle-mods.patch index 9225baf6fb..88af876aa6 100644 --- a/gfx/angle/src/tests/third_party/gpu_test_expectations/angle-mods.patch +++ b/gfx/angle/src/tests/third_party/gpu_test_expectations/angle-mods.patch @@ -1,6 +1,8 @@ -diff -u -rupN gpu_test_expectations_reverted/HowToMakeChanges.md gpu_test_expectations/HowToMakeChanges.md ---- gpu_test_expectations_reverted/HowToMakeChanges.md 1969-12-31 16:00:00.000000000 -0800 -+++ gpu_test_expectations/HowToMakeChanges.md 2015-09-14 13:47:33.000000000 -0700 +diff --git a/src/tests/third_party/gpu_test_expectations/HowToMakeChanges.md b/src/tests/third_party/gpu_test_expectations/HowToMakeChanges.md +new file mode 100644 +index 0000000..7817d0a +--- /dev/null ++++ b/src/tests/third_party/gpu_test_expectations/HowToMakeChanges.md @@ -0,0 +1,22 @@ +Because the ```gpu_test_expectations``` directory is based on parts of Chromium's ```gpu/config`` +directory, we want to keep a patch of the changes added to make it compile with ANGLE. This @@ -19,15 +21,17 @@ diff -u -rupN gpu_test_expectations_reverted/HowToMakeChanges.md gpu_test_expect + + * ```git apply -R angle-mods.patch```, ```git add . -u```, ```git commit``` + * Copy over Chromium files, ```git add . -u```, ```git commit``` -+ * ```git revert HEAD~2``` ++ * ```git revert HEAD~``` + * ```rm angle-mods.patch``` -+ * ```git diff HEAD~1 (`)ls(`) > angle-mods.patch```,```git add angle-mods.patch```, ```git commit --amend``` ++ * ```git diff HEAD~ (`)ls(`) > angle-mods.patch```,```git add angle-mods.patch```, ```git commit --amend``` + * ```git rebase -i``` to squash the three patches into one. + -diff -u -rupN gpu_test_expectations_reverted/angle_config.h gpu_test_expectations/angle_config.h ---- gpu_test_expectations_reverted/angle_config.h 1969-12-31 16:00:00.000000000 -0800 -+++ gpu_test_expectations/angle_config.h 2015-09-14 13:55:24.000000000 -0700 -@@ -0,0 +1,57 @@ +diff --git a/src/tests/third_party/gpu_test_expectations/angle_config.h b/src/tests/third_party/gpu_test_expectations/angle_config.h +new file mode 100644 +index 0000000..3d7b20c +--- /dev/null ++++ b/src/tests/third_party/gpu_test_expectations/angle_config.h +@@ -0,0 +1,62 @@ +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be @@ -59,13 +63,18 @@ diff -u -rupN gpu_test_expectations_reverted/angle_config.h gpu_test_expectation +typedef int64_t int64; +typedef uint64_t uint64; + -+// Shim Chromium's base by importing functions in the bsae namespace. ++// Shim Chromium's base by importing functions in the base namespace. +namespace base +{ -+ using angle::HexStringToUInt; -+ using angle::ReadFileToString; ++ using angle::kWhitespaceASCII; ++ using angle::TRIM_WHITESPACE; ++ using angle::KEEP_WHITESPACE; ++ using angle::SPLIT_WANT_ALL; ++ using angle::SPLIT_WANT_NONEMPTY; + using angle::SplitString; + using angle::SplitStringAlongWhitespace; ++ using angle::HexStringToUInt; ++ using angle::ReadFileToString; + + // StringPrintf is called differently in ANGLE but using cannot change + // the name of the imported function. Use a define to change the name. @@ -85,9 +94,10 @@ diff -u -rupN gpu_test_expectations_reverted/angle_config.h gpu_test_expectation +#endif + +#endif -diff -u -rupN gpu_test_expectations_reverted/gpu_info.cc gpu_test_expectations/gpu_info.cc ---- gpu_test_expectations_reverted/gpu_info.cc 2015-09-14 13:46:54.000000000 -0700 -+++ gpu_test_expectations/gpu_info.cc 2015-07-21 08:22:04.000000000 -0700 +diff --git a/src/tests/third_party/gpu_test_expectations/gpu_info.cc b/src/tests/third_party/gpu_test_expectations/gpu_info.cc +index 23d5216..4f279a4 100644 +--- a/src/tests/third_party/gpu_test_expectations/gpu_info.cc ++++ b/src/tests/third_party/gpu_test_expectations/gpu_info.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -97,7 +107,7 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_info.cc gpu_test_expectations/g namespace { -@@ -17,31 +17,6 @@ void EnumerateGPUDevice(const gpu::GPUIn +@@ -17,31 +17,6 @@ void EnumerateGPUDevice(const gpu::GPUInfo::GPUDevice& device, enumerator->EndGPUDevice(); } @@ -129,19 +139,17 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_info.cc gpu_test_expectations/g } // namespace namespace gpu { -@@ -66,23 +41,16 @@ GPUInfo::GPUInfo() - sandboxed(false), - process_crash_count(0), +@@ -68,9 +43,6 @@ GPUInfo::GPUInfo() + in_process_gpu(true), basic_info_state(kCollectInfoNone), + context_info_state(kCollectInfoNone), -#if defined(OS_WIN) -- context_info_state(kCollectInfoNone), -- dx_diagnostics_info_state(kCollectInfoNone) { --#else - context_info_state(kCollectInfoNone) { +- dx_diagnostics_info_state(kCollectInfoNone), -#endif + jpeg_decode_accelerator_supported(false) { } - GPUInfo::~GPUInfo() { } +@@ -78,11 +50,9 @@ GPUInfo::~GPUInfo() { } void GPUInfo::EnumerateFields(Enumerator* enumerator) const { struct GPUInfoKnownFields { @@ -153,8 +161,8 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_info.cc gpu_test_expectations/g GPUDevice gpu; std::vector secondary_gpus; uint64 adapter_luid; -@@ -109,14 +77,6 @@ void GPUInfo::EnumerateFields(Enumerator - int process_crash_count; +@@ -110,14 +80,6 @@ void GPUInfo::EnumerateFields(Enumerator* enumerator) const { + bool in_process_gpu; CollectInfoResult basic_info_state; CollectInfoResult context_info_state; -#if defined(OS_WIN) @@ -165,10 +173,10 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_info.cc gpu_test_expectations/g - video_decode_accelerator_supported_profiles; - VideoEncodeAcceleratorSupportedProfiles - video_encode_accelerator_supported_profiles; + bool jpeg_decode_accelerator_supported; }; - // If this assert fails then most likely something below needs to be updated. -@@ -134,15 +94,9 @@ void GPUInfo::EnumerateFields(Enumerator +@@ -136,15 +98,9 @@ void GPUInfo::EnumerateFields(Enumerator* enumerator) const { EnumerateGPUDevice(secondary_gpu, enumerator); enumerator->BeginAuxAttributes(); @@ -184,24 +192,25 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_info.cc gpu_test_expectations/g enumerator->AddInt64("adapterLuid", adapter_luid); enumerator->AddString("driverVendor", driver_vendor); enumerator->AddString("driverVersion", driver_version); -@@ -168,14 +122,6 @@ void GPUInfo::EnumerateFields(Enumerator - enumerator->AddInt("processCrashCount", process_crash_count); +@@ -171,14 +127,7 @@ void GPUInfo::EnumerateFields(Enumerator* enumerator) const { + enumerator->AddBool("inProcessGpu", in_process_gpu); enumerator->AddInt("basicInfoState", basic_info_state); enumerator->AddInt("contextInfoState", context_info_state); -#if defined(OS_WIN) - enumerator->AddInt("DxDiagnosticsInfoState", dx_diagnostics_info_state); -#endif -- // TODO(kbr): add dx_diagnostics on Windows. + // TODO(kbr): add dx_diagnostics on Windows. - for (const auto& profile : video_decode_accelerator_supported_profiles) - EnumerateVideoDecodeAcceleratorSupportedProfile(profile, enumerator); - for (const auto& profile : video_encode_accelerator_supported_profiles) - EnumerateVideoEncodeAcceleratorSupportedProfile(profile, enumerator); + enumerator->AddBool("jpegDecodeAcceleratorSupported", + jpeg_decode_accelerator_supported); enumerator->EndAuxAttributes(); - } - -diff -u -rupN gpu_test_expectations_reverted/gpu_info.h gpu_test_expectations/gpu_info.h ---- gpu_test_expectations_reverted/gpu_info.h 2015-09-14 13:46:54.000000000 -0700 -+++ gpu_test_expectations/gpu_info.h 2015-07-21 08:22:04.000000000 -0700 +diff --git a/src/tests/third_party/gpu_test_expectations/gpu_info.h b/src/tests/third_party/gpu_test_expectations/gpu_info.h +index d6f61fd..0a7f9aa 100644 +--- a/src/tests/third_party/gpu_test_expectations/gpu_info.h ++++ b/src/tests/third_party/gpu_test_expectations/gpu_info.h @@ -11,13 +11,7 @@ #include #include @@ -265,25 +274,25 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_info.h gpu_test_expectations/gp // Primary GPU, for exmaple, the discrete GPU in a dual GPU machine. GPUDevice gpu; -@@ -210,17 +177,7 @@ struct GPU_EXPORT GPUInfo { +@@ -213,17 +180,7 @@ struct GPU_EXPORT GPUInfo { // if the collection fails or not. CollectInfoResult basic_info_state; CollectInfoResult context_info_state; -#if defined(OS_WIN) - CollectInfoResult dx_diagnostics_info_state; - +- - // The information returned by the DirectX Diagnostics Tool. - DxDiagNode dx_diagnostics; -#endif -- + - VideoDecodeAcceleratorSupportedProfiles - video_decode_accelerator_supported_profiles; - VideoEncodeAcceleratorSupportedProfiles - video_encode_accelerator_supported_profiles; - // Note: when adding new members, please remember to update EnumerateFields - // in gpu_info.cc. + bool jpeg_decode_accelerator_supported; -@@ -238,8 +195,6 @@ struct GPU_EXPORT GPUInfo { + // Note: when adding new members, please remember to update EnumerateFields +@@ -243,8 +200,6 @@ struct GPU_EXPORT GPUInfo { virtual void AddInt(const char* name, int value) = 0; virtual void AddString(const char* name, const std::string& value) = 0; virtual void AddBool(const char* name, bool value) = 0; @@ -292,9 +301,10 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_info.h gpu_test_expectations/gp // Markers indicating that a GPUDevice is being described. virtual void BeginGPUDevice() = 0; -diff -u -rupN gpu_test_expectations_reverted/gpu_test_config.cc gpu_test_expectations/gpu_test_config.cc ---- gpu_test_expectations_reverted/gpu_test_config.cc 2015-09-14 13:46:54.000000000 -0700 -+++ gpu_test_expectations/gpu_test_config.cc 2015-09-15 10:12:23.000000000 -0700 +diff --git a/src/tests/third_party/gpu_test_expectations/gpu_test_config.cc b/src/tests/third_party/gpu_test_expectations/gpu_test_config.cc +index a7cc4b4..f8571d4 100644 +--- a/src/tests/third_party/gpu_test_expectations/gpu_test_config.cc ++++ b/src/tests/third_party/gpu_test_expectations/gpu_test_config.cc @@ -2,18 +2,193 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -498,7 +508,7 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_test_config.cc gpu_test_expecta #endif namespace gpu { -@@ -292,21 +467,5 @@ bool GPUTestBotConfig::CurrentConfigMatc +@@ -295,21 +470,5 @@ bool GPUTestBotConfig::CurrentConfigMatches( return false; } @@ -520,9 +530,10 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_test_config.cc gpu_test_expecta - } // namespace gpu -diff -u -rupN gpu_test_expectations_reverted/gpu_test_config.h gpu_test_expectations/gpu_test_config.h ---- gpu_test_expectations_reverted/gpu_test_config.h 2015-09-14 13:46:54.000000000 -0700 -+++ gpu_test_expectations/gpu_test_config.h 2015-08-11 13:26:51.000000000 -0700 +diff --git a/src/tests/third_party/gpu_test_expectations/gpu_test_config.h b/src/tests/third_party/gpu_test_expectations/gpu_test_config.h +index b5431e6..4cbc2c0 100644 +--- a/src/tests/third_party/gpu_test_expectations/gpu_test_config.h ++++ b/src/tests/third_party/gpu_test_expectations/gpu_test_config.h @@ -8,9 +8,7 @@ #include #include @@ -534,7 +545,7 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_test_config.h gpu_test_expectat namespace gpu { -@@ -132,9 +130,6 @@ class GPU_EXPORT GPUTestBotConfig : publ +@@ -134,9 +132,6 @@ class GPU_EXPORT GPUTestBotConfig : public GPUTestConfig { // Check if this bot's config matches |config_data| or any of the |configs|. static bool CurrentConfigMatches(const std::string& config_data); static bool CurrentConfigMatches(const std::vector& configs); @@ -544,9 +555,11 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_test_config.h gpu_test_expectat }; } // namespace gpu -diff -u -rupN gpu_test_expectations_reverted/gpu_test_config_mac.h gpu_test_expectations/gpu_test_config_mac.h ---- gpu_test_expectations_reverted/gpu_test_config_mac.h 1969-12-31 16:00:00.000000000 -0800 -+++ gpu_test_expectations/gpu_test_config_mac.h 2015-09-15 10:12:29.000000000 -0700 +diff --git a/src/tests/third_party/gpu_test_expectations/gpu_test_config_mac.h b/src/tests/third_party/gpu_test_expectations/gpu_test_config_mac.h +new file mode 100644 +index 0000000..da22bd6 +--- /dev/null ++++ b/src/tests/third_party/gpu_test_expectations/gpu_test_config_mac.h @@ -0,0 +1,28 @@ +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. @@ -576,9 +589,11 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_test_config_mac.h gpu_test_expe +gpu::GPUInfo::GPUDevice GetActiveGPU(); + +#endif // GPU_TEST_EXPECTATIONS_GPU_TEST_CONFIG_MAC_H_ -diff -u -rupN gpu_test_expectations_reverted/gpu_test_config_mac.mm gpu_test_expectations/gpu_test_config_mac.mm ---- gpu_test_expectations_reverted/gpu_test_config_mac.mm 1969-12-31 16:00:00.000000000 -0800 -+++ gpu_test_expectations/gpu_test_config_mac.mm 2015-09-15 12:49:51.000000000 -0700 +diff --git a/src/tests/third_party/gpu_test_expectations/gpu_test_config_mac.mm b/src/tests/third_party/gpu_test_expectations/gpu_test_config_mac.mm +new file mode 100644 +index 0000000..8cbd498 +--- /dev/null ++++ b/src/tests/third_party/gpu_test_expectations/gpu_test_config_mac.mm @@ -0,0 +1,67 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be @@ -647,9 +662,10 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_test_config_mac.mm gpu_test_exp + return gpu; +} + -diff -u -rupN gpu_test_expectations_reverted/gpu_test_expectations_parser.cc gpu_test_expectations/gpu_test_expectations_parser.cc ---- gpu_test_expectations_reverted/gpu_test_expectations_parser.cc 2015-09-14 13:46:54.000000000 -0700 -+++ gpu_test_expectations/gpu_test_expectations_parser.cc 2015-08-24 13:06:46.000000000 -0700 +diff --git a/src/tests/third_party/gpu_test_expectations/gpu_test_expectations_parser.cc b/src/tests/third_party/gpu_test_expectations/gpu_test_expectations_parser.cc +index 6dac74e..23c4d8c 100644 +--- a/src/tests/third_party/gpu_test_expectations/gpu_test_expectations_parser.cc ++++ b/src/tests/third_party/gpu_test_expectations/gpu_test_expectations_parser.cc @@ -2,14 +2,43 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -701,7 +717,7 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_test_expectations_parser.cc gpu namespace gpu { -@@ -144,9 +173,9 @@ const char* kErrorMessage[] = { +@@ -146,9 +175,9 @@ const char* kErrorMessage[] = { }; Token ParseToken(const std::string& word) { @@ -713,7 +729,7 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_test_expectations_parser.cc gpu return kConfigGPUDeviceID; for (int32 i = 0; i < kNumberOfExactMatchTokens; ++i) { -@@ -174,10 +203,10 @@ bool NamesMatching(const std::string& re +@@ -176,10 +205,10 @@ bool NamesMatching(const std::string& ref, const std::string& test_name) { GPUTestExpectationsParser::GPUTestExpectationsParser() { // Some sanity check. @@ -728,7 +744,7 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_test_expectations_parser.cc gpu } GPUTestExpectationsParser::~GPUTestExpectationsParser() { -@@ -202,8 +231,8 @@ bool GPUTestExpectationsParser::LoadTest +@@ -204,8 +233,8 @@ bool GPUTestExpectationsParser::LoadTestExpectations(const std::string& data) { return rt; } @@ -739,7 +755,7 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_test_expectations_parser.cc gpu entries_.clear(); error_messages_.clear(); -@@ -393,7 +422,7 @@ bool GPUTestExpectationsParser::ParseLin +@@ -399,7 +428,7 @@ bool GPUTestExpectationsParser::ParseLine( stage++; break; default: @@ -748,7 +764,7 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_test_expectations_parser.cc gpu break; } } -@@ -481,7 +510,7 @@ bool GPUTestExpectationsParser::UpdateTe +@@ -488,7 +517,7 @@ bool GPUTestExpectationsParser::UpdateTestConfig( config->set_api(config->api() | kTokenData[token].flag); break; default: @@ -757,9 +773,10 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_test_expectations_parser.cc gpu break; } return true; -diff -u -rupN gpu_test_expectations_reverted/gpu_test_expectations_parser.h gpu_test_expectations/gpu_test_expectations_parser.h ---- gpu_test_expectations_reverted/gpu_test_expectations_parser.h 2015-09-14 13:46:54.000000000 -0700 -+++ gpu_test_expectations/gpu_test_expectations_parser.h 2015-07-21 08:22:04.000000000 -0700 +diff --git a/src/tests/third_party/gpu_test_expectations/gpu_test_expectations_parser.h b/src/tests/third_party/gpu_test_expectations/gpu_test_expectations_parser.h +index a69f7e9..a112700 100644 +--- a/src/tests/third_party/gpu_test_expectations/gpu_test_expectations_parser.h ++++ b/src/tests/third_party/gpu_test_expectations/gpu_test_expectations_parser.h @@ -8,10 +8,8 @@ #include #include @@ -773,7 +790,7 @@ diff -u -rupN gpu_test_expectations_reverted/gpu_test_expectations_parser.h gpu_ namespace gpu { -@@ -32,7 +30,7 @@ class GPU_EXPORT GPUTestExpectationsPars +@@ -32,7 +30,7 @@ class GPU_EXPORT GPUTestExpectationsParser { // save all the entries. Otherwise, generate error messages. // Return true if parsing succeeds. bool LoadTestExpectations(const std::string& data); diff --git a/gfx/angle/src/tests/third_party/gpu_test_expectations/angle_config.h b/gfx/angle/src/tests/third_party/gpu_test_expectations/angle_config.h index a1d58fac40..3d7b20cc6c 100644 --- a/gfx/angle/src/tests/third_party/gpu_test_expectations/angle_config.h +++ b/gfx/angle/src/tests/third_party/gpu_test_expectations/angle_config.h @@ -29,13 +29,18 @@ typedef uint32_t uint32; typedef int64_t int64; typedef uint64_t uint64; -// Shim Chromium's base by importing functions in the bsae namespace. +// Shim Chromium's base by importing functions in the base namespace. namespace base { - using angle::HexStringToUInt; - using angle::ReadFileToString; + using angle::kWhitespaceASCII; + using angle::TRIM_WHITESPACE; + using angle::KEEP_WHITESPACE; + using angle::SPLIT_WANT_ALL; + using angle::SPLIT_WANT_NONEMPTY; using angle::SplitString; using angle::SplitStringAlongWhitespace; + using angle::HexStringToUInt; + using angle::ReadFileToString; // StringPrintf is called differently in ANGLE but using cannot change // the name of the imported function. Use a define to change the name. diff --git a/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_info.cc b/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_info.cc index c8e2988d5a..4f279a4290 100644 --- a/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_info.cc +++ b/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_info.cc @@ -40,8 +40,10 @@ GPUInfo::GPUInfo() direct_rendering(true), sandboxed(false), process_crash_count(0), + in_process_gpu(true), basic_info_state(kCollectInfoNone), - context_info_state(kCollectInfoNone) { + context_info_state(kCollectInfoNone), + jpeg_decode_accelerator_supported(false) { } GPUInfo::~GPUInfo() { } @@ -75,8 +77,10 @@ void GPUInfo::EnumerateFields(Enumerator* enumerator) const { bool direct_rendering; bool sandboxed; int process_crash_count; + bool in_process_gpu; CollectInfoResult basic_info_state; CollectInfoResult context_info_state; + bool jpeg_decode_accelerator_supported; }; // If this assert fails then most likely something below needs to be updated. @@ -120,8 +124,12 @@ void GPUInfo::EnumerateFields(Enumerator* enumerator) const { enumerator->AddBool("directRendering", direct_rendering); enumerator->AddBool("sandboxed", sandboxed); enumerator->AddInt("processCrashCount", process_crash_count); + enumerator->AddBool("inProcessGpu", in_process_gpu); enumerator->AddInt("basicInfoState", basic_info_state); enumerator->AddInt("contextInfoState", context_info_state); + // TODO(kbr): add dx_diagnostics on Windows. + enumerator->AddBool("jpegDecodeAcceleratorSupported", + jpeg_decode_accelerator_supported); enumerator->EndAuxAttributes(); } diff --git a/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_info.h b/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_info.h index 35e791e11a..0a7f9aa592 100644 --- a/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_info.h +++ b/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_info.h @@ -173,11 +173,16 @@ struct GPU_EXPORT GPUInfo { // Number of GPU process crashes recorded. int process_crash_count; + // True if the GPU is running in the browser process instead of its own. + bool in_process_gpu; + // The state of whether the basic/context/DxDiagnostics info is collected and // if the collection fails or not. CollectInfoResult basic_info_state; CollectInfoResult context_info_state; + bool jpeg_decode_accelerator_supported; + // Note: when adding new members, please remember to update EnumerateFields // in gpu_info.cc. diff --git a/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_config.cc b/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_config.cc index da689cfb79..f8571d4b20 100644 --- a/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_config.cc +++ b/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_config.cc @@ -236,6 +236,8 @@ GPUTestConfig::OS GetCurrentOS() { return GPUTestConfig::kOsMacMavericks; case 10: return GPUTestConfig::kOsMacYosemite; + case 11: + return GPUTestConfig::kOsMacElCapitan; } } #elif defined(OS_ANDROID) @@ -358,6 +360,7 @@ bool GPUTestBotConfig::IsValid() const { case kOsMacMountainLion: case kOsMacMavericks: case kOsMacYosemite: + case kOsMacElCapitan: case kOsLinux: case kOsChromeOS: case kOsAndroid: diff --git a/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_config.h b/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_config.h index 588b88485f..4cbc2c07e5 100644 --- a/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_config.h +++ b/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_config.h @@ -28,12 +28,14 @@ class GPU_EXPORT GPUTestConfig { kOsMacMountainLion = 1 << 7, kOsMacMavericks = 1 << 8, kOsMacYosemite = 1 << 9, + kOsMacElCapitan = 1 << 10, kOsMac = kOsMacLeopard | kOsMacSnowLeopard | kOsMacLion | - kOsMacMountainLion | kOsMacMavericks | kOsMacYosemite, - kOsLinux = 1 << 10, - kOsChromeOS = 1 << 11, - kOsAndroid = 1 << 12, - kOsWin10 = 1 << 13, + kOsMacMountainLion | kOsMacMavericks | kOsMacYosemite | + kOsMacElCapitan, + kOsLinux = 1 << 11, + kOsChromeOS = 1 << 12, + kOsAndroid = 1 << 13, + kOsWin10 = 1 << 14, kOsWin = kOsWinXP | kOsWinVista | kOsWin7 | kOsWin8 | kOsWin10, }; diff --git a/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_expectations_parser.cc b/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_expectations_parser.cc index 099fabaa00..23c4d8c453 100644 --- a/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_expectations_parser.cc +++ b/gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_expectations_parser.cc @@ -68,6 +68,7 @@ enum Token { kConfigMacMountainLion, kConfigMacMavericks, kConfigMacYosemite, + kConfigMacElCapitan, kConfigMac, kConfigLinux, kConfigChromeOS, @@ -121,6 +122,7 @@ const TokenInfo kTokenData[] = { {"mountainlion", GPUTestConfig::kOsMacMountainLion}, {"mavericks", GPUTestConfig::kOsMacMavericks}, {"yosemite", GPUTestConfig::kOsMacYosemite}, + {"elcapitan", GPUTestConfig::kOsMacElCapitan}, {"mac", GPUTestConfig::kOsMac}, {"linux", GPUTestConfig::kOsLinux}, {"chromeos", GPUTestConfig::kOsChromeOS}, @@ -216,8 +218,8 @@ bool GPUTestExpectationsParser::LoadTestExpectations(const std::string& data) { entries_.clear(); error_messages_.clear(); - std::vector lines; - base::SplitString(data, '\n', &lines); + std::vector lines = base::SplitString( + data, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); bool rt = true; for (size_t i = 0; i < lines.size(); ++i) { if (!ParseLine(lines[i], i + 1)) @@ -263,8 +265,9 @@ GPUTestExpectationsParser::GetErrorMessages() const { bool GPUTestExpectationsParser::ParseConfig( const std::string& config_data, GPUTestConfig* config) { DCHECK(config); - std::vector tokens; - base::SplitStringAlongWhitespace(config_data, &tokens); + std::vector tokens = base::SplitString( + config_data, base::kWhitespaceASCII, base::KEEP_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); for (size_t i = 0; i < tokens.size(); ++i) { Token token = ParseToken(tokens[i]); @@ -281,6 +284,7 @@ bool GPUTestExpectationsParser::ParseConfig( case kConfigMacMountainLion: case kConfigMacMavericks: case kConfigMacYosemite: + case kConfigMacElCapitan: case kConfigMac: case kConfigLinux: case kConfigChromeOS: @@ -313,8 +317,9 @@ bool GPUTestExpectationsParser::ParseConfig( bool GPUTestExpectationsParser::ParseLine( const std::string& line_data, size_t line_number) { - std::vector tokens; - base::SplitStringAlongWhitespace(line_data, &tokens); + std::vector tokens = base::SplitString( + line_data, base::kWhitespaceASCII, base::KEEP_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); int32 stage = kLineParserBegin; GPUTestExpectationEntry entry; entry.line_number = line_number; @@ -338,6 +343,7 @@ bool GPUTestExpectationsParser::ParseLine( case kConfigMacMountainLion: case kConfigMacMavericks: case kConfigMacYosemite: + case kConfigMacElCapitan: case kConfigMac: case kConfigLinux: case kConfigChromeOS: @@ -458,6 +464,7 @@ bool GPUTestExpectationsParser::UpdateTestConfig( case kConfigMacMountainLion: case kConfigMacMavericks: case kConfigMacYosemite: + case kConfigMacElCapitan: case kConfigMac: case kConfigLinux: case kConfigChromeOS: diff --git a/gfx/layers/TransactionIdAllocator.h b/gfx/layers/TransactionIdAllocator.h index 25e4d924d9..f6940a35e0 100644 --- a/gfx/layers/TransactionIdAllocator.h +++ b/gfx/layers/TransactionIdAllocator.h @@ -28,6 +28,13 @@ public: */ virtual uint64_t GetTransactionId() = 0; + /** + * Return the transaction id that for the last non-revoked transaction. + * This allows the caller to tell whether a composite was triggered by + * a paint that occurred after a call to TransactionId(). + */ + virtual uint64_t LastTransactionId() const = 0; + /** * Notify that all work (including asynchronous composites) * for a given transaction id has been completed. diff --git a/gfx/layers/client/ClientLayerManager.cpp b/gfx/layers/client/ClientLayerManager.cpp index 7903b109c7..5926fde162 100644 --- a/gfx/layers/client/ClientLayerManager.cpp +++ b/gfx/layers/client/ClientLayerManager.cpp @@ -419,11 +419,11 @@ ClientLayerManager::DidComposite(uint64_t aTransactionId, if (aTransactionId) { nsIWidgetListener *listener = mWidget->GetWidgetListener(); if (listener) { - listener->DidCompositeWindow(aCompositeStart, aCompositeEnd); + listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd); } listener = mWidget->GetAttachedWidgetListener(); if (listener) { - listener->DidCompositeWindow(aCompositeStart, aCompositeEnd); + listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd); } mTransactionIdAllocator->NotifyTransactionCompleted(aTransactionId); } diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 4206882a7b..2f1bb16d8e 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -2161,7 +2161,7 @@ nsPresContext::EnsureSafeToHandOutCSSRules() } void -nsPresContext::FireDOMPaintEvent(nsInvalidateRequestList* aList) +nsPresContext::FireDOMPaintEvent(nsInvalidateRequestList* aList, uint64_t aTransactionId) { nsPIDOMWindow* ourWindow = mDocument->GetWindow(); if (!ourWindow) @@ -2186,7 +2186,7 @@ nsPresContext::FireDOMPaintEvent(nsInvalidateRequestList* aList) // (hopefully it won't, or we're likely to get an infinite loop! At least // it won't be blocking app execution though). RefPtr event = - NS_NewDOMNotifyPaintEvent(eventTarget, this, nullptr, eAfterPaint, aList); + NS_NewDOMNotifyPaintEvent(eventTarget, this, nullptr, eAfterPaint, aList, aTransactionId); // Even if we're not telling the window about the event (so eventTarget is // the chrome event handler, not the window), the window is still @@ -2383,6 +2383,7 @@ nsPresContext::ClearNotifySubDocInvalidationData(ContainerLayer* aContainer) struct NotifyDidPaintSubdocumentCallbackClosure { uint32_t mFlags; + uint64_t mTransactionId; bool mNeedsAnotherDidPaintNotification; }; static bool @@ -2394,7 +2395,7 @@ NotifyDidPaintSubdocumentCallback(nsIDocument* aDocument, void* aData) if (shell) { nsPresContext* pc = shell->GetPresContext(); if (pc) { - pc->NotifyDidPaintForSubtree(closure->mFlags); + pc->NotifyDidPaintForSubtree(closure->mFlags, closure->mTransactionId); if (pc->IsDOMPaintEventPending()) { closure->mNeedsAnotherDidPaintNotification = true; } @@ -2406,8 +2407,10 @@ NotifyDidPaintSubdocumentCallback(nsIDocument* aDocument, void* aData) class DelayedFireDOMPaintEvent : public nsRunnable { public: DelayedFireDOMPaintEvent(nsPresContext* aPresContext, - nsInvalidateRequestList* aList) + nsInvalidateRequestList* aList, + uint64_t aTransactionId) : mPresContext(aPresContext) + , mTransactionId(aTransactionId) { MOZ_ASSERT(mPresContext->GetContainerWeak(), "DOMPaintEvent requested for a detached pres context"); @@ -2418,17 +2421,19 @@ public: // The pres context might have been detached during the delay - // that's fine, just don't fire the event. if (mPresContext->GetContainerWeak()) { - mPresContext->FireDOMPaintEvent(&mList); + mPresContext->FireDOMPaintEvent(&mList, mTransactionId); } return NS_OK; } RefPtr mPresContext; + uint64_t mTransactionId; nsInvalidateRequestList mList; }; void -nsPresContext::NotifyDidPaintForSubtree(uint32_t aFlags) +nsPresContext::NotifyDidPaintForSubtree(uint32_t aFlags, uint64_t aTransactionId, + const mozilla::TimeStamp& aTimeStamp) { if (IsRoot()) { static_cast(this)->CancelDidPaintTimer(); @@ -2455,11 +2460,12 @@ nsPresContext::NotifyDidPaintForSubtree(uint32_t aFlags) } if (aFlags & nsIPresShell::PAINT_COMPOSITE) { nsCOMPtr ev = - new DelayedFireDOMPaintEvent(this, &mUndeliveredInvalidateRequestsBeforeLastPaint); + new DelayedFireDOMPaintEvent(this, &mUndeliveredInvalidateRequestsBeforeLastPaint, + aTransactionId); nsContentUtils::AddScriptRunner(ev); } - NotifyDidPaintSubdocumentCallbackClosure closure = { aFlags, false }; + NotifyDidPaintSubdocumentCallbackClosure closure = { aFlags, aTransactionId, false }; mDocument->EnumerateSubDocuments(NotifyDidPaintSubdocumentCallback, &closure); if (!closure.mNeedsAnotherDidPaintNotification && diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index 3990ba1383..dbc3bf7c3d 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -925,8 +925,9 @@ public: // aRect is in device pixels void NotifyInvalidation(const nsIntRect& aRect, uint32_t aFlags); // aFlags are nsIPresShell::PAINT_ flags - void NotifyDidPaintForSubtree(uint32_t aFlags); - void FireDOMPaintEvent(nsInvalidateRequestList* aList); + void NotifyDidPaintForSubtree(uint32_t aFlags, uint64_t aTransactionId = 0, + const mozilla::TimeStamp& aTimeStamp = mozilla::TimeStamp()); + void FireDOMPaintEvent(nsInvalidateRequestList* aList, uint64_t aTransactionId); // Callback for catching invalidations in ContainerLayers // Passed to LayerProperties::ComputeDifference diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index b752adbe44..473e8c0df3 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -1997,6 +1997,12 @@ nsRefreshDriver::GetTransactionId() return mPendingTransaction; } +uint64_t +nsRefreshDriver::LastTransactionId() const +{ + return mPendingTransaction; +} + void nsRefreshDriver::RevokeTransactionId(uint64_t aTransactionId) { diff --git a/layout/base/nsRefreshDriver.h b/layout/base/nsRefreshDriver.h index e215313b58..229d9cd419 100644 --- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -306,7 +306,8 @@ public: static bool GetJankLevels(mozilla::Vector& aJank); // mozilla::layers::TransactionIdAllocator - virtual uint64_t GetTransactionId() override; + uint64_t GetTransactionId() override; + uint64_t LastTransactionId() const override; void NotifyTransactionCompleted(uint64_t aTransactionId) override; void RevokeTransactionId(uint64_t aTransactionId) override; mozilla::TimeStamp GetTransactionStart() override; diff --git a/layout/build/nsContentDLF.cpp b/layout/build/nsContentDLF.cpp index 2ffc776332..25eea1a0ea 100644 --- a/layout/build/nsContentDLF.cpp +++ b/layout/build/nsContentDLF.cpp @@ -212,7 +212,8 @@ nsContentDLF::CreateInstance(const char* aCommand, aExtraInfo, aDocListener, aDocViewer); } - if (mozilla::DecoderTraits::ShouldHandleMediaType(contentType.get())) { + if (mozilla::DecoderTraits::ShouldHandleMediaType(contentType.get(), + /* DecoderDoctorDiagnostics* */ nullptr)) { return CreateDocument(aCommand, aChannel, aLoadGroup, aContainer, kVideoDocumentCID, diff --git a/netwerk/mime/nsMimeTypes.h b/netwerk/mime/nsMimeTypes.h index e07dd238a0..73f455bb33 100644 --- a/netwerk/mime/nsMimeTypes.h +++ b/netwerk/mime/nsMimeTypes.h @@ -156,6 +156,7 @@ #define VIDEO_MPEG "video/mpeg" #define VIDEO_MP4 "video/mp4" +#define VIDEO_QUICKTIME "video/quicktime" #define VIDEO_RAW "video/x-raw-yuv" #define VIDEO_OGG "video/ogg" #define VIDEO_WEBM "video/webm" diff --git a/testing/web-platform/meta/dom/historical.html.ini b/testing/web-platform/meta/dom/historical.html.ini index 52decf4acc..90ef48287f 100644 --- a/testing/web-platform/meta/dom/historical.html.ini +++ b/testing/web-platform/meta/dom/historical.html.ini @@ -9,12 +9,3 @@ [Historical DOM features must be removed: createCDATASection] expected: FAIL - [Node member must be nuked: namespaceURI] - expected: FAIL - - [Node member must be nuked: prefix] - expected: FAIL - - [Node member must be nuked: localName] - expected: FAIL - diff --git a/testing/web-platform/meta/dom/interfaces.html.ini b/testing/web-platform/meta/dom/interfaces.html.ini index 81de6540e5..02fd90f1c8 100644 --- a/testing/web-platform/meta/dom/interfaces.html.ini +++ b/testing/web-platform/meta/dom/interfaces.html.ini @@ -48,15 +48,6 @@ [DocumentFragment interface: calling queryAll(DOMString) on document.createDocumentFragment() with too few arguments must throw TypeError] expected: FAIL - [Element interface: attribute namespaceURI] - expected: FAIL - - [Element interface: attribute prefix] - expected: FAIL - - [Element interface: attribute localName] - expected: FAIL - [Element interface: operation query(DOMString)] expected: FAIL diff --git a/toolkit/components/mediasniffer/nsMediaSniffer.cpp b/toolkit/components/mediasniffer/nsMediaSniffer.cpp index eb1fcb1a75..b288593617 100644 --- a/toolkit/components/mediasniffer/nsMediaSniffer.cpp +++ b/toolkit/components/mediasniffer/nsMediaSniffer.cpp @@ -43,7 +43,7 @@ nsMediaSnifferEntry sFtypEntries[] = { PATTERN_ENTRY("\xFF\xFF\xFF", "3gp", VIDEO_3GPP), // Could be 3gp4, 3gp5, ... PATTERN_ENTRY("\xFF\xFF\xFF\xFF", "M4A ", AUDIO_MP4), PATTERN_ENTRY("\xFF\xFF\xFF\xFF", "M4P ", AUDIO_MP4), - PATTERN_ENTRY("\xFF\xFF\xFF\xFF", "qt ", VIDEO_MP4), + PATTERN_ENTRY("\xFF\xFF\xFF\xFF", "qt ", VIDEO_QUICKTIME), }; static bool MatchesBrands(const uint8_t aData[4], nsACString& aSniffedType) diff --git a/view/nsView.cpp b/view/nsView.cpp index b63463d05d..49cf167053 100644 --- a/view/nsView.cpp +++ b/view/nsView.cpp @@ -1078,7 +1078,8 @@ nsView::DidPaintWindow() } void -nsView::DidCompositeWindow(const TimeStamp& aCompositeStart, +nsView::DidCompositeWindow(uint64_t aTransactionId, + const TimeStamp& aCompositeStart, const TimeStamp& aCompositeEnd) { nsIPresShell* presShell = mViewManager->GetPresShell(); @@ -1088,7 +1089,8 @@ nsView::DidCompositeWindow(const TimeStamp& aCompositeStart, nsPresContext* context = presShell->GetPresContext(); nsRootPresContext* rootContext = context->GetRootPresContext(); MOZ_ASSERT(rootContext, "rootContext must be valid."); - rootContext->NotifyDidPaintForSubtree(nsIPresShell::PAINT_COMPOSITE); + rootContext->NotifyDidPaintForSubtree(nsIPresShell::PAINT_COMPOSITE, aTransactionId, + aCompositeEnd); // If the two timestamps are identical, this was likely a fake composite // event which wouldn't be terribly useful to display. diff --git a/view/nsView.h b/view/nsView.h index a69909c3aa..94bd4fed34 100644 --- a/view/nsView.h +++ b/view/nsView.h @@ -386,7 +386,8 @@ public: virtual bool PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion aRegion) override; virtual void DidPaintWindow() override; - virtual void DidCompositeWindow(const mozilla::TimeStamp& aCompositeStart, + virtual void DidCompositeWindow(uint64_t aTransactionId, + const mozilla::TimeStamp& aCompositeStart, const mozilla::TimeStamp& aCompositeEnd) override; virtual void RequestRepaint() override; virtual nsEventStatus HandleEvent(mozilla::WidgetGUIEvent* aEvent, diff --git a/widget/nsIWidgetListener.cpp b/widget/nsIWidgetListener.cpp index 97a05bb33a..488e5814ca 100644 --- a/widget/nsIWidgetListener.cpp +++ b/widget/nsIWidgetListener.cpp @@ -107,7 +107,8 @@ nsIWidgetListener::DidPaintWindow() } void -nsIWidgetListener::DidCompositeWindow(const TimeStamp& aCompositeStart, +nsIWidgetListener::DidCompositeWindow(uint64_t aTransactionId, + const TimeStamp& aCompositeStart, const TimeStamp& aCompositeEnd) { } diff --git a/widget/nsIWidgetListener.h b/widget/nsIWidgetListener.h index 550c5e5c17..05e2f58b89 100644 --- a/widget/nsIWidgetListener.h +++ b/widget/nsIWidgetListener.h @@ -140,7 +140,8 @@ public: */ virtual void DidPaintWindow(); - virtual void DidCompositeWindow(const mozilla::TimeStamp& aCompositeStart, + virtual void DidCompositeWindow(uint64_t aTransactionId, + const mozilla::TimeStamp& aCompositeStart, const mozilla::TimeStamp& aCompositeEnd); /**